JavaSE
Java优缺点:
优点:
-
跨平台性:Java程序可以在不同的操作系统上运行,只需在不同的平台上安装Java虚拟机(JVM)即可。
-
面向对象:Java是一种面向对象的编程语言,允许开发者按照面向对象的思维方式设计和实现程序,提高了代码的重用性和可维护性。
-
安全性:Java有严格的访问控制和内存管理机制,可以防止内存泄漏和程序崩溃,并提供安全性相关的特性,如安全沙箱机制和自动内存管理。
-
多线程支持:Java具有多线程处理的能力,可以同时执行多个任务,提高程序性能。
-
强大的工具支持:Java拥有各种开发工具和框架,如集成开发环境(IDE)、调试器和性能分析工具,可以提高开发效率和代码质量。
缺点:
-
垃圾回收机制:Java使用垃圾回收机制来自动管理内存,但这也会导致一些性能问题和额外的开销。
-
执行速度较慢:由于Java是解释执行的语言,相比于编译语言执行速度较慢。
-
内存消耗较大:Java虚拟机会消耗大量的内存资源,对于某些嵌入式或资源受限的环境,可能不适合使用Java。
-
复杂性:Java具有较高的技术门槛和较复杂的语法规则,对于初学者来说学习曲线较陡峭。
-
安全性问题:虽然Java提供了一些安全特性,但仍然存在一些安全漏洞和风险,需要开发者注意安全编码的问题。
命名规范:
- 所有变量,方法,类名:见名知意;
- 类成员变量:首字母小写和驼峰原则,如:monthSalary;
- 局部变量:首字母小写和驼峰原则;
- 常量:大写字母和下划线:MAX_VALUE;
- 类名:首字母大写和驼峰原则;
- 方法名:首字母小写和驼峰原则。
Idea快捷键:
1.生成main方法:输入main -> 回车
2.生成输出语句:sout -> 回车
3.将变量名放到输出语句中:
a.变量名.sout
b.变量名.soutv -> 带字符串平拼接格式的输出方式-> 输出格式好看
快捷键 | 功能 |
---|---|
Alt+Enter |
导入包,自动修正代码(重中之重) |
Ctrl+Y |
删除光标所在行 |
Ctrl+D |
复制光标所在行的内容,插入光标位置下面 |
Ctrl+Alt+L |
格式化代码 |
Ctrl+/ |
单行注释 |
Ctrl+Shift+/ |
选中代码注释,多行注释,再按取消注释 |
Alt+Shift+上下箭头 |
移动当前代码行 |
Java SE
1.循环
for循环扩展
语法格式:
for(声明类型:表达式){
循环体;
}
该用法主要用于数组和集合。
声明类型:声明新的局部变量,该变量类型必须数组元素的类型匹配,其作用域限定在循环体内。
表达式:表达式的值是要访问的数组名也可以是返回值为数组的方法。
例子:
int[] number = {1, 2, 3, 4, 5, 6};
for (int x:number){
System.out.print(x);
}
//结果:123456
2.数组
2.1 初始化
静态初始化:
//int[]表示定义了一个存放int类型数据的数组
int[] array = new int[]{1,2,3,4,5};
//也可以简化为
int[] array = {1,2,3,4,5};
//方括号也可以放在标识符的后面
int array[] = new int[]{1,2,3,4,5};
动态初始化:
int[] array = new int[5];//先定义一个自动初始化的数组,但要规定他的长度
array[0] = 1;
array[1] = 2;
array[2] = 3;
array[3] = 4;
array[4] = 5;
//可以用 数组名称[下角标] = 某个值 的方式对数组的某个元素进行赋值
tips:
(1)一个数组只能存放一种类型的数据,这种数据类型在一开始就被规定好了
(2)一个数组在初始化后的长度是固定的,最大的下角标是长度-1,超过这个范围则会报错
(3)动态初始化的初始赋值依据不同数据类型而不同,int-->0 ,double-->0.0,String-->null , boolean -->false,char-->’’
可使用for each循环
2.2 Arrays类
Arrays类是java.util包中的一个关于数组的方法集,最常用的方法是sort和binarySearch
sort进行升序排列:
int[] array = new int[]{3,1,7,3,4};
Arrays.sort(array);//使用Arrays.sort方法
for(int i : array){
System.out.print(i);
}
//输出:13347
binarySearch方法是在已经升序排序的数组中搜索并返回目标值的索引值,如果不存在则返回(查找值的插入点)-1,注意存在多个目标值时,返回的索引值是这些目标值的索引值的中值(向上取整)
int[] array = new int[]{3,1,7,3,4};
Arrays.sort(array);//先进行升序排序
//1 3 3 4 7
//使用binarySearch
System.out.println(Arrays.binarySearch(array,7));//存在,返回值应当为4
System.out.println(Arrays.binarySearch(array,3));//存在,返回值应当为2
System.out.println(Arrays.binarySearch(array,5));//不存在,应该返回-5
System.out.println(Arrays.binarySearch(array,-1));//存在,返回值应当为-1
二维数组内存:
2.3 可变参数
1.需求:
定义一个方法,实现n个整数相加
2.分析:
方法参数位置,只明确了参数的类型,但是不明确参数个数,此时就可以定义成可变参数
使用方法
1.定义格式:
数据类型...变量名
2.注意:
a.可变参数的本质是一个数组
b.参数位置不能连续写多个可变参数,而且当可变参数和其他普通参数一起使用时,可变参数需要放到参数列表最后
public class Demo01Var {
public static void main(String[] args) {
sum(1,2,3,4,5);
sum1(1,1,2,3,4);
}
public static void sum(int...arr){
int sum = 0;
for (int i = 0; i < arr.length; i++) {
sum+=arr[i];
}
System.out.println(sum);
}
public static void sum1(int i,int...arr){
}
}
2.4 对象数组
需求:定义一个长度为3的数组,存储3个Person对象,遍历数组,将三个Person对象中的属性值获取出来
//定义数组
Person[] arr = new Person[3];
//创建三个对象
Person p1 = new Person("金莲",26);
Person p2 = new Person("涛哥",18);
Person p3 = new Person("张三",20);
//将三个对象保存到数组中
arr[0] = p1;
arr[1] = p2;
arr[2] = p3;
3.内存:
3.1 栈(Stack)
主要运行方法,方法的运行都会去栈内存中运行,运行完毕之后,需要"弹栈",腾空间
3.2 堆(Heap)
每new一次,都会在堆内存中开辟空间,并为此空间自动分配一个地址值
堆中的数据都是有默认值的
整数:0
小数:0.0
字符: '\u0000'
布尔:false
引用:null
3.3 方法区(Method Area)
代码的"预备区",记录了类的信息以及方法的信息
方法区中主要保存class文件及其中的信息
代码运行之前,需要先进内存(方法区)
3.4 本地方法栈(了解):
——专门运行native方法(本地方法)
本地方法可以理解为对java功能的扩充
有很多功能java语言实现不了,所以就需要依靠本地方法完成
3.5 寄存器(了解) -> 跟CPU有关
4.方法
注意事项
1.方法不调用不执行
2.方法的执行顺序只和调用顺序有关
3.方法之间不能互相嵌套
4.void不能和[return 结果]共存,但是void能和[return]共存
a.void:代表没有返回值
b.return 结果:就代表有返回值了
先将结果返回,然后结束方法
c.return:仅仅代表结束方法,不代表有返回值
5.一个方法中不能连续写多个return(也就是说一个方法不能都多个返回值)
6.调用方法的时候要看看下面有没有这个方法,没有的方法直接调用会报错
方法参数:
基本类型做方法参数传递,传递的是值,不是变量本身
方法运行:压栈
方法运行完毕:弹栈 -> 释放栈内存
引用数据类型做方法参数传递时,传递的是地址值
5.类和对象
5.1 类
1.测试类:带main方法的类,主要是运行代码的
2.实体类:是一类事物的抽象表示形式
世间万物的分类:比如: 人类 狗类 猫类 鼠标类
组成部分:
1.属性(成员变量):这一类事物有啥
a.定义位置:类中方法外
b.作用范围:作用于当前类
c.定义格式: 数据类型 变量名
d.默认值:
整数:0
小数:0.0
字符:'\u0000'
布尔:false
引用:null
2.行为(成员方法):这一类事物都能干啥
只需要将模块六所学的方法中的static干掉,其他的都一样
5.2 对象
1.概述:一类事物的具体体现
2.使用:
a.导包: import 包名.类名
如果两个类在同一个包下,想要使用对方的成员不需要导包
如果两个类不在同一个包下,想要使用对方的成员需要导包
特殊包:java.lang -> 使用lang包下的类不需要导包 -> String
b.创建对象:想要使用哪个类中的成员,就new哪个类的对象
类名 对象名 = new 类名() -> 比如: Person person = new Person()
c.调用成员(成员变量,成员方法) -> 想要使用哪个类中的成员,就用哪个类的对象去点哪个成员
对象名.成员变量名 = 值
对象名.方法名() -> 调用的是无参无返回值方法
对象名.方法名(实参) -> 调用的是有参无返回值方法
数据类型 变量名 = 对象名.方法名() -> 调用的是无参有返回值方法
数据类型 变量名 = 对象名.方法名(实参) -> 调用的是有参有返回值方法
5.3 匿名对象
new Person();
1.所谓的匿名对象:其实就是没有等号左边的部分,只有等号右边的部分(对象)
2.使用:
new 对象().成员
new Person().eat();
3.注意:
a.如果我们只想单纯的调用一个方法,让方法执行,我们可以考虑使用匿名对象
b.但是如果涉及到赋值,千万不要用匿名对象
5.4 对象的内存
对象属性、方法都存在堆heap中,当对象中方法被调用时,方法在栈中运行
5.5 成员变量与局部变量
1.定义位置不同(重点)
a.成员变量:类中方法外
b.局部变量:定义在方法之中或者参数位置
2.初始化值不同(重点)
a.成员变量:有默认值的,所以不用先手动赋值,就可以直接使用
b.局部变量:是没有默认值的,所以需要先手动赋值,再使用
3.作用范围不同(重点)
a.成员变量:作用于整个类
b.局部变量:只作用于自己所在的方法,其他方法使用不了
4.内存位置不同(了解)
a.成员变量:在堆中,跟着对象走
b.局部变量:在栈中,跟着方法走
5.生命周期不同(了解)
a.成员变量:随着对象的创建而产生,随着对象的消失而消失
b.局部变量:随着方法的调用而产生,随着方法的调用完毕而消失
5.6 static关键字
1.概述:static是一个静态关键字
2.使用:
a.修饰一个成员变量:
static 数据类型 变量名
b.修饰一个方法:
修饰符 static 返回值类型 方法名(形参){
方法体
return 结果
}
3.调用静态成员:
类名直接调用(不用new对象)
4.静态成员特点:
a.静态成员属于类成员,不属于对象成员(非静态的成员属于对象成员)
b.静态成员会随着类的加载而加载
c.静态成员优先于非静态成员存在在内存中
d.凡是根据静态成员所在的类创建出来的对象,都可以共享这个静态成员
经过static修饰后成员访问特点:
1.在静态方法中能直接访问非静态成员嘛? 不能
想要调用的话:new对象调用
2.在非静态方法中能直接访问静态成员嘛? 能
a.同类:
直接调用
类名调用
b.不同类:
类名调用
3.在静态方法中能直接访问静态成员嘛?能
a.同类:
直接调用
类名调用
b.不同类:
类名调用
4.在非静态方法中能直接访问非静态成员嘛?能
a.同类:
直接调用
new对象调用
b.不同类:
new对象调用
总结:
1.不管在不在同一个类中,非静态成员都可以new对象调用
2.不管在不在同一个类中,静态成员都可以类名调用
问题1:既然static成员那么好使(类名直接调用),那么我们在实际开发中,能不能将所有的成员都定义成静态的呢? 不能 原因:由于静态成员会随着类的加载而加载,如果将所有的成员都变成静态的,那么类一加载,静态成员都会进内存,会大量占用内存空间 问题2:那么静态成员都啥时候定义呢? 一般情况下,我们在抽取工具类的时候可以将工具类中的所有成员都定义成静态的 问题3:啥时候定义工具类? 比如我们在写代码的过程中,发现有的功能在反复实现,代码一样,功能一样,此时就可以抽取出来,形成工具类
5.7 final关键字
1.修饰类
1.格式:
public final class 类名{}
2.特点:
被final修饰的类不能被继承
2.修饰方法
1.格式:
修饰符 final 返回值类型 方法名(形参){
方法体
return 结果
}
2.特点:
被final修饰的方法,不能被重写
3.注意:
"final和abstract不能同时修饰一个方法"
3.修饰局部变量
1.格式:
final 数据类型 变量名 = 值
2.特点:
被final修饰的变量不能二次赋值
4.修饰对象
1.格式:
final 数据类型 对象名 = new 对象();
2.特点:
"被final修饰的对象,地址值不能改变",但是对象中的属性值可以改变
5.修饰成员变量
1.格式:
final 数据类型 变量名 = 值
2.特点:
a.需要手动赋值
b.不能二次赋值
5.8 代码块
1.格式:
{
代码
}
2.执行特点:优先于构造方法执行,每new一次,就会执行一次
静态代码块:
1.格式:
static{
代码
}
2.执行特点:
静态代码块"优先于"构造代码块和构造方法执行的,而且"只执行一次"
代码块使用场景:
如果想让一些数据最先初始化,而且只需要初始化一次,就可以将这些数据放到静态代码块中
6.封装
隐藏对象内部的复杂性,只对外提供公开、公共的接口,便于外界调用,从而提高了系统的可扩展性、可维护性、安全性
通俗来说,把该隐藏的隐藏起来(细节),把该暴露的暴露出来(对外提供的供别人使用的接口),这就是封装思想
1.问题:
定义成员变量,只要是new出来对象,就可以随便调用,随便赋值,哪怕是不合理的值我们也挡不住,怎么办?
将属性封装起来(隐藏细节)
a.关键字:private(私有化的) -> 被private修饰的成员只能在本类中使用,在别的类中使用不了
b.注意:
将代码放到一个方法中,也是封装的体现
一个成员被private修饰也是封装的体现,只不过private最具代表性
c.private的使用:
修饰成员变量:private 数据类型 变量名
修饰方法:将public改成private,其他的都一样
2.问题:属性被私有化了,外界直接调用不了了,那么此时属性就不能直接赋值取值了,所以需要提供公共的接口
get/set方法
set方法:为属性赋值
get方法:获取属性值
6.1 构造方法
1.概述:方法名和类名一致并且能初始化对象的方法
2.分类:
a.无参构造:没有参数
b.有参构造:有参数,参数是为指定的属性赋值
c.满参构造:给所有属性赋值
3.特点:
a.方法名和类名一致
b.没有返回值,连void都没有
6.2 标准JavaBean
JavaBean 是 Java语言编写类的一种标准规范。符合
JavaBean` 的类,要求:
(1)类必须是具体的(非抽象 abstract)和公共的,public class 类名
(2)并且具有无参数的构造方法,有参构造
(3)成员变量私有化,并提供用来操作成员变量的set
和get
方法。
com.atguigu.controller -> 专门放和页面打交道的类(表现层)
com.atguigu.service -> 专门放业务处理的类 (业务层)
com.atguigu.dao -> 专门放和数据库打交道的类(持久层)
com.atguigu.pojo -> 专门放javabean类
com.atguigu.utils -> 专门放工具类
快速生成标准JavaBean通用快捷键:alt+insert
JavaBean与数据库相关联:
7.继承
7.1 简介:
1.父类怎么形成的:我们的定义了多个类,发现这些类中有很多重复性的代码,我们就定义了一个父类,将相同的代码抽取出来放到父类中,其他的类直接继承这个父类,就可以直接使用父类中的内容了
2.怎么去继承: extends
子类 extends 父类
3.注意:
a.子类可以继承父类中私有和非私有成员,但是"不能使用父类中私有成员"
b.构造方法不能继承
4.继承怎么学:
a.继承不要从是否"拥有"方面来学习
要从是否能"使用"方面来学习
继承中的成员变量和成员方法:
成员变量访问特点:看等号左边是谁,先调用谁中的成员,子类没有,找父类
成员方法访问特点:看new的是谁,先调用谁中的方法
7.2 方法重写
1.概述:子类中有一个和父类方法名以及参数列表相同的方法
2.前提:继承
3.访问:看new的是谁,先调用谁中的,如果new的是子类,调用调用子类重写的方法,子类没有,找父类
4.检测是否为重写方法:在该方法上写
@Override
tips:
1.子类重写父类方法之后,权限必须要保证大于等于父类权限(权限指的是访问权限)
public -> protected -> 默认 -> private
2.子类方法重写父类方法,方法名和参数列表要一样
3.私有方法不能被重写,构造方法不能被重写,静态方法不能被重写
4.子类重写父类方法之后,返回值类型应该是父类方法返回值类型的子类类型
7.3 super和this
继承中的构造方法:
1.注意:new子类对象时,会先初始化父类(先走父类无参构造方法)
2.原因:
每个构造方法的第一行,默认都会有一个super(),不写jvm自动提供一个
super()代表的是父类无参构造
super:
1.概述:代表的是父类引用
2.作用:可以调用父类中的成员
3.使用:
a.调用父类构造方法-> 在子类中的构造中写
super() -> 调用父类无参构造
super(实参) -> 调用父类有参构造
b.调用父类成员变量:
super.成员变量名
c.调用父类成员方法:
super.成员方法名(实参)
this:
1.this概述:代表的是当前对象(哪个对象调用的this所在的方法,this就代表哪个对象)
2.作用:
a.区分重名的成员变量和局部变量
b.调用当前对象中的成员
3.使用:
a.调用当前对象的构造:在构造中写
this():调用当前对象的无参构造
this(实参):调用当前对象的有参构造
b.调用当前对象的成员变量:
this.成员变量名
c.调用当前对象的成员方法:
this.成员方法名(实参)
4.注意:
不管是super还是this,只要在构造中使用,都必须在第一行,所以二者不能同时手写出来
7.4 继承特点
1.继承只支持单继承,不能多继承
public class A extends B,C{} -> 错误
2.继承支持多层继承
public class A extends B{}
public class B extends C{}
3.一个父类可以有多个子类
public class A extends C{}
public class B extends C{}
4.构造方法不能继承,也不能重写
私有方法可以继承,但是不能被重写
静态方法可以继承,但是不能被重写
问题:如何为父类中private的成员变量赋值
public class Employee {
private String name;
public Employee() {
}
public Employee(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
1.利用set赋值
public class Teacher extends Employee{
Teacher teacher = new Teacher();
teacher.setName("源源");
}
2.利用构造方法赋值
public class Teacher extends Employee{
public Teacher(String name){
super(name);
}
Teacher teacher = new Teacher("源源");
}
8.抽象
8.1 简介
1.抽象类怎么来的?
抽取共性方法,放到父类中,发现方法没法实现,因为每个子类对此方法的实现方式细节不一样
此时方法体说不清道不明,可以定义成抽象方法
抽象方法所在的类一定是抽象类
2.关键字: abstract
3.抽象方法:
修饰符 abstract 返回值类型 方法名(参数);
4.抽象类:
public abstract class 类名{}
5.注意:
a.抽象方法所在的类一定是抽象类
b.抽象类中不一定非得有抽象方法
c.子类继承父类之后,需要重写父类
中所有的抽象方法,不然编译报错
d.抽象类不能new对象,只能通过new子类对象调动重写方法
6.可以将抽象类看成是一类事物的标准,要求只要是属于这一类的,都必须要拥有抽象类中的方法,必须要给我实现,怎么证明拥有了,怎么证明实现了呢?-> 重写
至于这个方法怎么实现,就看子类重写之后怎么写方法体了
8.2抽象的注意事项
1.抽象类不能直接new对象,只能创建非抽象子类的对象
2.抽象类中不一定非得有抽象方法,但是抽象方法所在的类一定抽象类
3.抽象类的子类,必须重写父类中的所有抽象方法,否则,编译报错,除非该子类也是抽象类
4.抽象类中可以有成员变量,构造,成员方法
5."抽象类中可以有构造方法,是供子类创建对象时,初始化父类属性使用的"
9.接口
9.1 介绍
1.接口:是一个引用数据类型,是一种标准,规则
2.关键字:
a.interface 接口
public interface 接口名{}
b.implements 实现
实现类 implements 接口名{}
3.接口中可以定义的成员:
a.jdk7以及之前:
抽象方法: public abstract -> 即使不写public abstract,默认也有
成员变量:public static final 数据类型 变量名 = 值-> 即使不写public static final,默认也有
final是最终的,被final修饰的变量不能二次赋值,所以我们一般将final修饰的变量视为常量
b.jdk8:
默认方法:public default 返回值类型 方法名(形参){}
静态方法:public static 返回值类型 方法名(形参){}
c.jdk9开始:
私有方法:
private的方法
1.定义接口:
public interface 接口名{}
2.实现:
public class 实现类类名 implements 接口名{}
3.使用:
a.实现类实现接口
b.重写接口中的抽象方法
c.创建实现类对象(接口不能直接new对象)
d.调用重写的方法
9.2 接口中的成员
1.抽象方法
1.定义格式:
public abstract 返回值类型 方法名(形参);
2.注意:
"不写public abstract 默认也有"
3.使用:
a.定义实现类,实现接口
b.重写抽象方法
c.创建实现类对象,调用重写的方法
public interface USB {
public abstract void open();
String close(); //默认就含有public abstract
}
public class Mouse implements USB{
@Override
public void open() {
System.out.println("鼠标打开");
}
@Override
public String close() {
return "鼠标关闭";
}
}
2.默认方法
1.格式:
public default 返回值类型 方法名(形参){
方法体
return 结果
}
2.使用:
a.定义实现类,实现接口
b.默认方法可重写,可不重写
c.创建实现类对象,调用默认方法
3.静态方法
1.定义格式:
public static 返回值类型 方法名(形参){
方法体
return 结果
}
2.使用:
接口名直接调用
默认方法和静态方法 -> 可以作为临时加的一个小功能来使用
4.成员变量
1.格式:
public static final 数据类型 变量名 = 值
2.相关知识点:final
final代表最终的,被它修饰的变量,不能二次赋值,可以视为常量
3.特点:
"不写public static final 默认也有"
4.使用:
接口名直接调用
5.注意:
a.被static final修饰的成员变量需要手动赋值
b.习惯上我们会"将static final修饰的成员变量名大写"
9.3 接口的特点
1.接口可以多继承 -> 一个接口可以继承多个接口
public interface InterfaceA extends InterfaceB,InterfaceC{}
2.接口可以多实现 -> 一个实现类可以实现一个或者多个接口
public class InterfaceImpl implements InterfaceA,InterfaceB{}
3.一个子类可以继承一个父类的同时实现一个或者多个接口
public class Zi extends Fu implements InterfaceA,InterfaceB{}
4.注意:
继承也好,实现接口也罢,只要是父类中或者接口的抽象方法,子类或者实现类都要重写
当一个类实现多个接口时,如果接口中的抽象方法有重名且参数一样的,只需要重写一次
当一个类实现多个接口时,如果多个接口中默认方法有重名的,且参数一样的,必须重写一次默认方法
public interface InterfaceA { public abstract void method(); public default void methodDef(){ System.out.println("我是接口A中的默认方法"); } } public interface InterfaceB { public abstract void method(); public default void methodDef() { System.out.println("我是接口B中的默认方法"); } } public class InterfaceImpl implements InterfaceA,InterfaceB{ @Override public void method() { System.out.println("重写的method方法"); } public void methodDef(){ System.out.println("重写的默认方法"); } }
9.4 接口和抽象类的区别
相同点:
a.都位于继承体系的顶端,用于被其他类实现或者继承
b.都不能new
c.都包含抽象方法,其子类或者实现类都必须重写这些抽象方法
不同点:
a.抽象类:一般作为父类使用,可以有成员变量,构造,成员方法,抽象方法等
b.接口:成员单一,一般抽取接口,抽取的都是方法,视为功能的大集合
c.类不能多继承,但是接口可以
10.多态
10.1 介绍
1.前提:
a.必须有子父类继承或者接口实现关系
b.必须有方法的重写(没有重写,多态没有意义),多态主要玩儿的是重写方法
c.new对象:父类引用指向子类对象
Fu fu = new Zi() -> 理解为大类型接收了一个小类型的数据 ->比如 double b = 10
2.注意:
多态下不能直接调用子类特有功能
10.2 多态成员访问特点
1.成员变量
看等号左边是谁,先调用谁中的成员变量
public class Fu {
int num = 1000;
}
public class Zi extends Fu{
int num = 100;
}
public class Test01 {
public static void main(String[] args) {
Fu fu = new Zi();
System.out.println(fu.num);
}
}
2.成员方法
看new的是谁,先调用谁中的成员方法,子类没有,找父类
10.3 多态的好处
1.问题描述:
如果使用原始方式new对象(等号左右两边一样),既能调用重写的,还能调用继承的,还能调用自己特有的成员
但是多态方式new对象,只能调用重写的,不能直接调用子类特有的成员,那为啥还要用多态呢?
2.多态方式和原始方式new对象的优缺点:
原始方式:
a.优点:既能调用重写的,还能调用父类非私有的,还能调用自己特有的
b.缺点:扩展性差
多态方式:
a.优点:扩展性强
b.缺点:不能直接调用子类特有功能
Fu fu = new Zi()
double b = 10;
b = 100L;
形参传递父类类型,调用此方法父类类型可以接收任意它的子类对象
传递哪个子类对象,就指向哪个子类对象,就调用哪个子类对象重写的方法
10.4 多态中的转型
1.向上转型
父类引用指向子类对象
好比是: double b = 1;
2.向下转型
1.向下转型:好比"强转",将大类型强制转成小类型
2.表现方式:
父类类型 对象名1 = new 子类对象() -> 向上转型 -> double b = 1
子类类型 对象名2 = (子类类型)对象名1 -> 向下转型 -> int i = (int)b
3.想要调用子类特有功能,我们就需要向下转型
3.转型可能出现的问题
1.如果等号左右两边类型不一致,会出现类型转换异常(ClassCastException)
2.解决:
"在向下转型之前,先判断类型"
3.怎么判断类型: instanceof
判断结果是boolean型
4.使用:
对象名 instanceof 类型 -> 判断的是关键字前面的对象是否符合关键字后面的类型
11.权限修饰符
public | protected | default(空的) | private | |
---|---|---|---|---|
同类 | yes | yes | yes | yes |
同包不同类 | yes | yes | yes | no |
不同包子父类 | yes | yes | no | no |
不同包非子父类 | yes | no | no | no |
public具有最大权限,private有最小权限
编写代码时,如果没有特殊的考虑,建议这样使用权限:
1.属性:用private -> 封装思想
2.成员方法public -> 便于调用
3.构造public -> 便于new对象
12.内部类
1.什么时候使用内部类:
当一个事物的内部,还有一个部分需要完整的结构去描述,而这个内部的完整结构又只为外部事物提供服务,那么整个内部的完成结构最好使用内部类
比如:人类都有心脏,人类本身需要用属性,行为去描述,那么人类内部的心脏也需要心脏特殊的属性和行为去描述,此时心脏就可以定义成内部类,人类中的一个心脏类
2.在java中允许一个类的定义位于另外一个类内部,前者就称之为内部类,后者称之为外部类
class A{
class B{
}
}
类A就是类B的外部类
类B就是类A的内部类
3.分类:
成员内部类(静态,非静态)
局部内部类
匿名内部类(重点)
12.1 静态成员内部类
1.格式:直接在定义内部类的时候加上static关键字
public class A{
static class B{
}
}
2.注意:
a.内部类可以定义属性,方法,构造等
b.静态内部类可以被final或者abstract修饰
被final修饰之后,不能被继承
被abstract修饰之后,不能new
c.静态内部类不能调用外部的非静态成员
d.内部类还可以被四种权限修饰符修饰
3.调用静态内部类成员:
外部类.内部类 对象名 = new 外部类.内部类()
12.2 非静态成员内部类
1.格式:
public class A{
class B{
}
}
2.注意:
a.内部类可以定义属性,方法,构造等
b.静态内部类可以被final或者abstract修饰
被final修饰之后,不能被继承
被abstract修饰之后,不能new
c."静态内部类不能调用外部的非静态成员"
d.内部类还可以被四种权限修饰符修饰
3.调用非静态内部类成员:
外部类.内部类 对象名 = new 外部类().new 内部类()
区分重名的外部类成员变量和内部类成员变量:
name // 内部类
this.name // 内部类
Person.this.name // 外部类
12.3 局部内部类
——可以定义在方法中,代码块中,构造中
1.接口作为方法参数传递和返回
1.接口作为方法参数,传递实参时,传递的是实现类对象
2.接口作为返回值类型返回,实际返回的是实现类对象
public class IntMain {
public static void main(String[] args) {
Mouse mouse = new Mouse();
method(mouse);
method2().open();
}
// 接口作为方法参数,传递实参时,传递的是实现类对象
public static void method(USB usb){ // USB usb = mouse -> 多态
usb.open();
}
// 接口作为返回值类型返回,实际返回的是实现类对象
public static USB method2(){
// Mouse mouse = new Mouse();
// return mouse;
return new Mouse();
}
}
2.抽象类作为方法参数和返回值
1.抽象类作为方法参数传递,传递实参时,传递的是其子类对象
2.抽象类作为方法返回值类型返回时,实际返回的是其子类对象
3.普通类做方法参数和返回值
普通类作为方法参数传递,传递的是对象
普通类作为方法返回值返回,返回的是对象
4.局部内部类实际操作
public interface USB {
void open();
}
public class Test01 {
public static void main(String[] args) {
USB usb = method();//USB usb = new Mouse()
usb.open();
}
public static USB method(){
//局部内部类
class Mouse implements USB{
@Override
public void open() {
System.out.println("鼠标打开");
}
}
return new Mouse();
}
}
12.4 匿名内部类(重点)
——没有显式声明出类名的内部类
1.问题描述:我们如果想实现接口,简单使用一次抽象方法,我们就需要创建一个实现类,实现这个接口,重写抽象方法,还要new实现类对象,所以我们在想如果就单纯的想使用一次接口中的方法,我们能不能不这么麻烦呢?可以
a.创建实现类,实现接口
b.重写方法
c.创建实现类对象
d.调用方法
2.如果就想单纯的使用一下接口中的方法,我们就没必要经过以上四步了,我们可以四合一
3.匿名内部类怎么学:就按照一种格式来学,这一种格式就代表了实现类对象或者子类对象
4.格式:
new 接口/抽象类(){
重写方法
}.重写的方法();
=================================
类名 对象名 = new 接口/抽象类(){
重写方法
}
对象名.重写的方法();
public class Test01 {
public static void main(String[] args) {
new USB(){
@Override
public void open() {
System.out.println("usb打开了");
}
@Override
public void close() {
System.out.println("usb关闭了");
}
}.open();
System.out.println("===================");
USB usb = new USB() {
@Override
public void open() {
System.out.println("USB打开了");
}
@Override
public void close() {
System.out.println("USB关闭了");
}
};
usb.open();
usb.close();
}
}
1.什么时候使用匿名内部类:
——当简单调用一次接口中的方法,我们就可以使用匿名内部类
2.将一种格式代表实现类对象或者子类对象来看待,来学习
3.匿名内部类会编译生成的,咱们不要管,我们只需要利用咱们讲的格式去new对象,调用重写的方法即可
1. 匿名内部类当参数传递
public interface USB {
void open();
}
public class Test01 {
public static void main(String[] args) {
method01(new USB() {
@Override
public void open() {
System.out.println("usb打开了");
}
});
}
public static void method01(USB usb){
usb.open();
}
}
2. 匿名内部类当返回值返回
public interface USB {
void open();
}
public class Test02 {
public static void main(String[] args) {
USB usb = method01();
usb.open();
}
public static USB method01(){
return new USB() {
@Override
public void open() {
System.out.println("USB打开了");
}
};
}
}
13.异常
13.1 介绍
1.概述:代码出现了不正常的现象;在java中,异常都是一个一个的类
13.2 异常处理
1.创建异常对象
1.关键字:throw
2.格式: throw new 异常
2.异常处理方式—throws
单个异常:
1.格式:在方法参数和方法体之间位置上写
throws 异常
2.意义:处理异常
将异常往上抛
多个异常:
1.格式:throws 异常1,异常2
2.注意:
如果throws的多个异常之间有子父类继承关系,我们可以直接throws父类异常
如果不知道多个异常之间是否有子父类继承关系,我们可以直接throws Exception
public class DemoException {
public static void main(String[] args) throws FileNotFoundException, IOException {
String s = "a.txt1";
// s = null;
add(s);
}
public static void add(String s) throws FileNotFoundException,IOException{
if(s==null){
throw new IOException("IO异常");
}
if(!s.endsWith(".txt")){
throw new FileNotFoundException("文件找不到!");
}
System.out.println("开始执行!");
}
}
3.异常处理方式—try...catch
单个异常:
1.格式:
try{
可能出现异常的代码
}catch(异常 对象名){
处理异常的代码-> 将来开发会将异常信息保存到日志文件中
}
多个异常:
1.格式:
try{
可能出现异常的代码
}catch(异常 对象名){
处理异常的代码-> 将来开发会将异常信息保存到日志文件中
}catch(异常 对象名){
处理异常的代码-> 将来开发会将异常信息保存到日志文件中
}catch(异常 对象名){
处理异常的代码-> 将来开发会将异常信息保存到日志文件中
}catch(异常 对象名){
处理异常的代码-> 将来开发会将异常信息保存到日志文件中
}...
2.注意:
如果catch的多个异常之间有子父类继承关系,我们可以直接catch父类异常
如果不知道多个异常之间是否有子父类继承关系,我们也可以直接catch Exception
public class TryException {
public static void main(String[] args) {
String s = "a.txt 1";
s = null;
try {
add(s);
}catch (FileNotFoundException e){
System.out.println(e);
}catch (IOException e){
System.out.println(e);
}finally {
System.out.println("一定会执行!");
}
}
public static void add(String s) throws FileNotFoundException,IOException{
if (s==null){
throw new IOException("IO异常!");
}
if(!s.endsWith(".txt")){
throw new FileNotFoundException("文件找不到!");
}
System.out.println("开始执行!");
}
}
4.finally关键字
1.概述:代表的是不管是否触发了异常,都会执行的代码块
特殊情况:如果之前执行了System.exit(0)终止当前正在执行的java虚拟机
2.使用:都是配合try...catch使用
try{
可能出现异常的代码
}catch(异常 对象名){
处理异常的代码-> 将来开发会将异常信息保存到日志文件中
}finally{
不管是否有异常,都会执行的代码
}
finally的使用场景:
1.关闭资源
2.原因:对象如果没有用了,GC(垃圾回收器)回收,用来回收堆内存中的垃圾,释放内存,但是有一些对象GC回收不了,比如:连接对象(Connection),IO流对象,Socket对象,这些对象GC回收不了,就需要我们自己手动回收,手动关闭
将来不能回收的对象new完之后,后续操作不管是否操作成功,是否有异常,我们都需要手动关闭,此时我们就可以将关闭资源的代码放到finally中
例如:
public class Test {
public static void main(String[] args) {
// finally回收
FileWriter fw = null;
try {
fw = new FileWriter("src\\Exception\\a.txt");
fw.write("哈哈哈");
}catch (IOException e){
throw new RuntimeException(e);
}finally {
if(fw!=null){
try {
fw.close();
System.out.println("成功回收!");
}catch (IOException e){
throw new RuntimeException(e);
}
}
}
}
}
5.抛异常Tips
子类父类异常:
1.如果父类中的方法抛了异常,那么子类重写之后要不要抛?
可抛可不抛
2.如果父类中的方法没有抛异常,那么子类重写之后要不要抛?
不要抛
throws和try的使用时机:
1.如果处理异常之后,还想让后续的代码正常执行,我们使用try...catch
2.如果方法之间是递进关系(调用),我们可以先throws,但是到了最后需要用try...catch做一个统一的异常处理
6.自定义异常
1.定义一个类
2.如果继承Exception 就是编译时期异常
3.如果继承RuntimeException,就是运行时期异常
public class LoginUserException extends Exception{
public LoginUserException(){
}
public LoginUserException(String message){
super(message);
}
}
public class DemoLoginException {
public static void main(String[] args) throws LoginUserException{
String username = "zangqiyue";
Scanner sc = new Scanner(System.in);
System.out.println("请输入您要登录的用户名:");
String name = sc.next();
if(name.equals(username)){
System.out.println("登录成功了");
}else{
try {
throw new LoginUserException("登录失败了,用户名或者密码有问题");
}catch (Exception e){
//System.out.println(e.toString());
//System.out.println(e.getMessage());
e.printStackTrace();
}
}
}
}
7.打印异常
Throwable类中的方法:
String toString() :输出异常类型和设置的异常信息
String getMessage(): 输出设置的异常信息
void printStackTrace():打印异常信息是最全的:包括异常类型,信息,以及出现的行数等
14.Object类
概述:所有类的根类(父类),所有的类都会直接或者间接继承Object类
14.1 toString
1.Object中的toString方法:返回该对象的字符串表示形式
public String toString() {
return getClass().getName() + "@" + Integer.toHexString(hashCode());
}
2.注意:
a.如果没有重写Object中的toString方法,直接输出对象名会默认调用Object中的toString方法,直接输出地址值
b.如果重写了Object中的toString方法,再输出地址值,重写没有意义,所以重写完toString之后,应该返回对象的内容
3.总结:
如果直接输出对象名不想输出地址值,就重写Object中的toString方法
14.2 equals
1.概述:比较两个对象的地址值是否相等
public boolean equals(Object obj) {
return (this == obj);
}
== 针对于基本数据类型来说,比较的是值
== 针对于引用数据类型来说,比较的是地址值
2.注意:
a.如果没有重写Object中的equals方法,那么就会调用Object中的equals方法,比较对象的地址值
b.如果重写了Object中的equals方法,那么就会调用重写后的equals方法,应该比较对象的内容
14.3 clone方法
1.作用:复制一个属性值一样的新对象
2.使用:
需要被克隆的对象实现Cloneable
重写clone方法
14.4 经典接口
1.java.lang.Comparable
我们知道基本数据类型的数据(除boolean类型外)需要比较大小的话,之间使用比较运算符即可,但是引用数据类型是不能直接使用比较运算符来比较大小的。那么,如何解决这个问题呢?
Java给所有引用数据类型的大小比较,指定了一个标准接口,就是java.lang.Comparable接口:
package java.lang;
public interface Comparable{
int compareTo(Object obj);
}
那么我们想要使得我们某个类的对象可以比较大小,怎么做呢?步骤:
第一步:哪个类的对象要比较大小,哪个类就实现java.lang.Comparable接口,并重写方法
- 方法体就是你要如何比较当前对象和指定的另一个对象的大小
第二步:对象比较大小时,通过对象调用compareTo方法,根据方法的返回值决定谁大谁小。
- this对象(调用compareTo方法的对象)减 指定对象(传入compareTo()的参数对象)大于0,返回正整数
- this对象(调用compareTo方法的对象)减 指定对象(传入compareTo()的参数对象)小于0 返回负整数
- this对象(调用compareTo方法的对象)减 指定对象(传入compareTo()的参数对象)等于0 返回零
代码示例:
public class Student implements Comparable{
private String name;
private int score;
public Student() {
}
public Student(String name, int score) {
this.name = name;
this.score = score;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getScore() {
return score;
}
public void setScore(int score) {
this.score = score;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", score=" + score +
'}';
}
/*
this:代表students[i]
o:代表students[i+1]
如果students[i].getScore()-students[i+1].getScore()>0
证明数组中的前面一个对象比后面一个对象的分数高
*/
@Override
public int compareTo(Object o) {
Student s = (Student) o;
return this.getScore()- s.getScore();
}
}
测试类
public class Test01 {
public static void main(String[] args) {
//创建一个数组
Student[] students = new Student[3];
Student s1 = new Student("张三", 100);
Student s2 = new Student("李四", 60);
Student s3 = new Student("王五", 80);
students[0] = s1;
students[1] = s2;
students[2] = s3;
for (int j = 0; j<students.length-1;j++){
for (int i = 0;i<students.length-1-j;i++){
//如果students[i]比students[i+1]大,就排序换位置
if (students[i].compareTo(students[i+1])>0){
Student temp = students[i];
students[i] = students[i+1];
students[i+1] = temp;
}
}
}
//遍历
for (int i = 0; i < students.length; i++) {
System.out.println(students[i]);
}
}
}
2.java.util.Comparator
思考:
(1)如果一个类,没有实现Comparable接口,而这个类你又不方便修改(例如:一些第三方的类,你只有.class文件,没有源文件),那么这样类的对象也要比较大小怎么办?
(2)如果一个类,实现了Comparable接口,也指定了两个对象的比较大小的规则,但是此时此刻我不想按照它预定义的方法比较大小,但是我又不能随意修改,因为会影响其他地方的使用,怎么办?
JDK在设计类库之初,也考虑到这种情况了,所以又增加了一个java.util.Comparator接口。
package java.util;
public interface Comparator{
int compare(Object o1,Object o2);
}
那么我们想要比较某个类的两个对象的大小,怎么做呢?步骤:
第一步:编写一个类,我们称之为比较器类型,实现java.util.Comparator接口,并重写方法
- 方法体就是你要如何指定的两个对象的大小
第二步:比较大小时,通过比较器类型的对象调用compare()方法,将要比较大小的两个对象作为compare方法的实参传入,根据方法的返回值决定谁大谁小。
- o1对象减o2大于0返回正整数
- o1对象减o2小于0返回负整数
- o1对象减o2等于0返回零
public class Student implements Comparator {
private String name;
private int score;
public Student() {
}
public Student(String name, int score) {
this.name = name;
this.score = score;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getScore() {
return score;
}
public void setScore(int score) {
this.score = score;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", score=" + score +
'}';
}
/*
o1代表students[i]
o2代表students[i+1]
如果o1的分数大于o2的分数-> compare方法返回正整数
如果o1的分数小于o2的分数-> compare方法返回负整数
如果o1的分数等于o2的分数-> compare方法返回0
*/
@Override
public int compare(Object o1, Object o2) {
Student s1 = (Student) o1;
Student s2 = (Student) o2;
return s1.getScore()-s2.getScore();
}
}
public class Test01 {
public static void main(String[] args) {
//创建一个数组
Student[] students = new Student[3];
Student s1 = new Student("张三", 100);
Student s2 = new Student("李四", 60);
Student s3 = new Student("王五", 80);
students[0] = s1;
students[1] = s2;
students[2] = s3;
Student student = new Student();
for (int j = 0; j<students.length-1;j++){
for (int i = 0;i<students.length-1-j;i++){
//如果students[i]比students[i+1]大,就排序换位置
if (student.compare(students[i],students[i+1])>0){
Student temp = students[i];
students[i] = students[i+1];
students[i+1] = temp;
}
}
}
//遍历
for (int i = 0; i < students.length; i++) {
System.out.println(students[i]);
}
}
}