面向对象
类和对象
class关键字
一个java文件可以有多个class,但是只能有一个public修饰的类,且必须与文件名相同
运行时,类存在方法区
new关键字
执行过程
- 分配对象空间,并将对象成员变量初始化为各式各样的零
- 执行属性值的显示初始化,即类中属性显示赋值
- 执行构造方法
- 返回对象的地址
运行时,对象在堆内存
匿名对象
在调用方法的时候只能使用唯一一次,省略了对象名,
内部类
成员内部类
- 位置:与成员变量地位一样,外部类内部,方法外部
- 编译:内部类也会单独编译成一个class字节码文件,命名以
外部类名$外部类名
形式命名 - 访问作用域:
- 内部类访问外部类成员,没有任何限制,需要
外部类名.this.成员名
形式访问 - 外部类访问内部类成员,需要借助内部类对象
- 其他地方访问内部类成员,需要借助内部类对象,创建内部类对象的语法
外部类名.内部类名 对象名 = new 外部类名().new 内部类名();
- 内部类访问外部类成员,没有任何限制,需要
静态内部类
- 是特殊的成员内部类,用static修饰
- 访问作用域:
- 内部类只能直接访问外部类的静态成员,访问非静态成员需要借助外部类对象
- 外部类访问内部静态成员,直接
内部类.成员
,非静态类成员,需要借助内部类对象 - 其他地方访问内部类普通成员,需要借助内部类对象,创建内部类对象的语法
外部类名.内部类名 对象名 = new 外部类名.内部类名();
,其他地方访问内部类的静态成员,直接外部类.内部类.成员
即可
局部内部类
- 位置:与局部变量地位一样,方法内
- 访问作用域
- 内部类访问方法局部变量,该变量必须是有效final的,即该变量的值只能赋值一次
- 方法中访问内部类成员,需要借助内部类对象
- 其他地方无法使用该内部类,只能在当前方法中使用
匿名内部类
- 是特殊的局部内部类
- 在创建对象的时候只能使用唯一一次,省略了实现类和实现类名
- 使用方式:
new 接口名(){...实现接口的方法};
- 左部接口名,对象自动上转型
- new是创建匿名内部类对象动作
- 右部接口名是要实现的接口
- {...}内部类的内容
- 编译:匿名内部类也会单独编译成一个class字节码文件,命名以
外部类名$编号
形式命名
Lambda表达式
- 只有接口并且只有一个抽象方法(可以有其他私有方法、默认方法和静态方法)时才能使用Lambda表达式,使用时必须注意上下文环境
- 使用方式:
接口名 对象名 = (参数列表)->{方法体}
- 省略模式
- 参数类型可省,但是要省都省,不能省一部分
- 参数只有一个下括号可省
- 方法体只有一条语句,大括号可省略,包括return
- 编译:Lambda表达式不会生成静态的字节码文件,在程序运行是动态生成字节码文件
方法引用
方法引用多用来作为方法作为参数时使用,也就是说参数要传入的是函数式接口,需要写Lambda表达式时,正好有写好的方法,就可以使用方法引用,将该方法作为要传入的方法
引用方式 | 格式 | 参数说明 |
---|---|---|
静态方法引用 | 类名::静态方法名 |
全部对应 |
成员方法引用(已实例化) | 对象::成员方法名 |
全部对应 |
类成员方法引用(未实例化) | 类名::成员方法名 |
首个参数是类实例 之后参数开始依次对应 |
构造方法引用 | 类名::new |
全部对应 |
类的权限修饰符
public | protected | (default) | private | |
---|---|---|---|---|
外部类 | yes | yes | ||
成员内部类 | yes | yes | yes | yes |
局部内部类 |
封装
构造方法
-
特殊的方法,再创建对象的时候调用,用来做对象的初始化,与类名一致,且不能写返回数据类型,也可以重载
public class Test { int id; public Test(int id){ this.id = id; } public Test(){ } // public int Test(){ } //报错,不能写返回数据类型 }
-
通过new关键字调用
Test test = new Test(); //调用构造方法 Test test1 = new Test(1); //调用构造方法的重载方法
-
相当于无返回值的方法,无需写返回类型和void,所以可以有空return,不能return某个值
public class Test { int id; public Test(int id){ this.id = id; return; //可空返回 } //public Test(){ return true;} //不能返回数据类型 }
-
若不写构造方法,编译器自动加一个无参构造方法,若写构造方法则不会自动添加
public class Test { int id; public Test(int id){ this.id = id; return;} public static void main(String[] args){ //new Test(); //报错,已写构造方法,不会自动添加 } } public class Test { int id; public static void main(String[] args){ new Test(); //不写构造方法,自动添加无参构造 } }
this关键字
- 类中若不写构造方法则编译器会自动添加一个无参构造,若写上则不会自动添加
- this本质是该类中所有方法的隐式参数,存放指向创建好的对象的指针,即指向当前对象
- 避免二义性来使用,区别成员变量和局部变量
- 只能在构造方法中调用重载的构造方法,且必须在第一行,必须有出口
- 不能在static修饰符修饰的方法中使用
static修饰符
- 用static修饰符修饰的属性和方法属于类,所有所有对象共享,再类加载后显示初始化
- 可以用类名调用,也可以通过对象调用,建议类名调用
- 静态方法中不能使用this关键字,静态方法只能直接调用静态方法和属性
- 非静态方法可以使用this关键字,能直接访问静态方法和属性,也能直接访问非静态方法和属性
访问控制修饰符
- 访问的两种形式
- 通过new出对象点的形式
- 子类继承父类。子类通过super关键字点的形式
private:同一个类两种形式都行,出了类都不行
default:同一个包两种形式都行,出了包都不行
protected:同一个包两种形式都行,出了包只有第二种形式可以,若又用static修饰后,则子类两种形式都行,若不是子类两种都不行
修饰符 | 同一个类 | 同一个包 | 不同包的子类 | 不同包非子类 |
---|---|---|---|---|
private | yes | |||
(default) | yes | yes | ||
protected | yes | yes | yes | |
public | yes | yes | yes | yes |
javaBean标准
- 必须是一个public类
- 必须有一个空的构造方法
- 属性使用private访问权限
- 为每个属性提供set/get方法访问相关属性,这些方法是public修饰
- getter/seter方法命名规范
- 若字段是布尔类型,把get改为is命名
- 若字段名第二个字母大写,直接使用get/set字段名即可
- 若字段名全部大写,直接使用get/set字段名即可
继承
extends关键字
- 父类别名超类、基类、派生类
- 类中只有单继承,没有多继承,接口有多继承
- 子类继承父类,可以得到父类的全部属性和方法(构造方法除外),但是能不能访问得看控制访问修饰符
- 未指定父类,默认父类是java.lang.Object
Object类
toString方法
public String toString() {
return getClass().getName() + "@" + Integer.toHexString(hashCode());
}
- 无参,返回全类名@对象的哈希地址
- 打印对象默认调用的是toString方法
- 重写toString方法,一般是打印输出对象的各个属性值
equals方法
public boolean equals(Object obj) {
return (this == obj);
}
- 参数是Object类型,返回两个对象地址是否相同的布尔值
- 重写equals方法,一般判断两个对象是否逻辑相等,就是各个属性是否相等
public boolean equals(Object o) {
if (this == o) return true;
//若是同一个对象地址,则是同一个对象,一定相等
if (o == null || getClass() != o.getClass()) return false;
//若传入对象是null,或两个对象不是同一个类,必定不相等
Test test = (Test) o; //将Object强制下转型为当前类的类型
return id == test.id &&
name.equals(testC.name);
//比较每个属性值,引用类型用equals方法,基本类型用==
//有可能引用数据类型封装了属性,使用get方法进行比较
//若有一项不等则认为两个对象逻辑上不一样,全部相等,则认为逻辑上全等
}
clone方法
- 将堆内存的对象复制一份,地址会不相同
- 由于使用protected修饰,所以需要子类重写该方法调用父类的clone方法才能实现克隆,要注意的是返回的是Object类形,在接收时需要强转
- 虽然已经可以调用克隆方法了,但是若不实现克隆标记接口
Cloneable
也会抛出异常
protected native Object clone() throws CloneNotSupportedException;
finalize方法
- 当对象被回收前调用,由于该方法抛出的是错误,而不是异常,原因是对于要回收的对象而言,回收方法内部抛出异常毫无影响
- 由于对象回收时调用该方法可能延时、死锁等问题导致对象回收失败,jdk1.9之后此方法不再建议使用
rotected void finalize() throws Throwable;
- jdk1.9后使用如下方法代替手动回收,解决回收延误,死锁等问题
import java.lang.ref.Cleaner;
public class Main{
public static void main(String[] args) {
Test c = new Test();
try(TestCleaner testCleaner = new TestCleaner(c)){
//创建指定对象的回收器,将要回收的对象传入,执行完这段代码后自动回收
}catch (Exception e){
e.printStackTrace();
}
}
}
class Test implements Runnable{
public Test() {
System.out.println("创建");
}
@Override
public void run() { //回收时调用该线程run方法
System.out.println("回收");
}
}
class TestCleaner implements AutoCloseable{
//回收器
private final Cleaner.Cleanable cleanable; //存放注册后的回收对象的盒子
public TestCleaner(Test c){ //将要回收的对象传入回收器的构造方法
final Cleaner cleaner = Cleaner.create(); //创建回收器对象
this.cleanable = cleaner.register(this,c); //使用回收器对象注册可该要回收对象,放入回收盒中
}
@Override
public void close() throws Exception {
//在close方法中执行回收操作
this.cleanable.clean(); //回收操作
}
}
Objects.equals方法
public static boolean equals(Object a,Object b) {
return a == b || (a != null && a.equals(b));
//该工具类方法能有效避免空指针异常比较两个对象是否相等
}
public static <T> T requireNonNull(T obj) {
//重载方法的第二个参数可以设置字符串类型的错误信息
if (obj == null)
throw new NullPointerException();
//该工具类方法能对对象进行空指针判断,若有空指针则抛出异常
//用于校验参数是否是空指针
return obj;
}
方法的重写
- 发生在继承关系中,子类重写父类方法
- 方法名和参数列表完全一致
- 返回值要求,子类返回值数据类型范围小于等于父类返回值范围
- 访问修饰符要求,子类方法权限大于等于父类方法访问权限
super关键字
- 子类构造方法中若没显式调用父类构造,则默认第一句
super();
调用父类无参构造方法,若父类还没有无参构造方法,则必须用super关键字显式调用父类构造,所以先执行父类构造,后执行子类构造 - super和this都是存放指向创建好的对象的指针,即指向当前对象,只不过super是指向父类成员的引用,this是指向子类成员的引用
- 中避免二义性来使用,区别子类成员和继承的父类成员和局部变量
- 只能在子类构造方法中调用父类构造方法,且必须在第一行
- 不能在static修饰符修饰的方法中使用
final修饰符
- 修饰变量:变为符号常量,对于引用类型来说,是地址不能改变
- 修饰成员变量:必须声明时赋值或所有重载构造中赋值,并且不能定义set该字段方法,变为符号常量
- 修饰方法:不能被重写,可以被重载(不能修饰抽象方法)
- 修饰类:不能被继承
多态
产生多态的条件
- 发生多态的三个必要条件:多个子类继承父类或实现类实现接口,多个子类都重写了父类方法,父类引用指向的是子类对象
- 多态发生在方法上的,引用指向的对象是谁,就是调用谁重写的方法,若当前没有重写该方法则才会向父类找,直到到Object类;编译看左部数据类型,运行看右部具体对象是谁
- 多态不会发生属性上,属性只会是当前引用对象类型的属性值,若当前没有重写该属性则才会向父类找,直到到Object类;编译和运行都看左部数据类型是谁
父类引用指向的是子类对象
- 该父类可以是普通父类
- 该父类可以是抽象类
- 该父类可以是接口
对象的转型
-
父类引用指向子类对象,即子类对象转为父类对象,一定是安全的,所以向上自动转型
- 向上自动转换:编译时就会检查出类型转换错误
-
将上转型后的引用无法使用子类方法,需要还原回子类对象才能使用,可能会转错是不安全的,所以需要强制转换,即父类引用指向子类对象还原为子类引用,向下强制转型
- 向下强制转换:运行时才会检查出错误,
ClassCastException
类型转换异常
- 向下强制转换:运行时才会检查出错误,
-
一般先判断父类引用指向的子类具体对象是谁,使用
父类引用指向子类对象的变量 instanceof 子类
,返回布尔值,若是返回true,再进行强制向下转型进行调用方法
代码块
普通代码块
在方法内,顺序执行,起到变量的块级作用域分隔作用
public static void main(String[] args) {
{ int a = 0; } //这个a遇到花括号后生命周期结束
//System.out.println(a); //报错,上个a的生命周期已结束,找不到a变量
int a = 0; //新声明的变量,作用域是从声明处到整个main方法结束
//{ int a = 0; } //报错,上一个a声明周期未结束,声明同名变量
}
构造代码块
- 在类内方法外,调用构造方法一次执行一次,构造方法之前执行
- 若父类也有构造代码块,则先执行父类的构造代码块
- 可以调用静态属性和方法,也可以访问非静态属性和方法
- 当有多个构造代码块时,各个构造代码块之间顺序执行
静态代码块
- 在类内方法外并且用static修饰,类加载时执行一次,构造代码块之前执行
- 若父类也有静态代码块,则先执行父类的静态代码块
- 只能访问静态方法和属性
- 当有多个静态代码块时,各个构造静态块之间顺序执行
抽象类和接口
abstract修饰符
-
不能与final修饰符同时使用,抽象方法本来就是为了子类实现,加上final没有意义
-
修饰类:该类就是抽象类
- 抽象类不能实例化,只能被继承
- 抽象类可以包含普通类的一切,但是构造方法不能用new来实例化,只能给子类用super调用
-
修饰方法:该方法就是抽象方法
- 抽象方法只能出现在抽象类和接口中
- 没有方法体,所以非抽象子类继承必须重写抽象方法
interface修饰符
代替class关键字声明的是接口
- 接口中只能定义常量(不写修饰符也默认加上
public static funal
)和抽象方法(不写修饰符也默认加上public abstract
) - jdk8 后新增:都拥有方法体了所以不能加
abstract
- 默认方法:
default
修饰符修饰的方法,拥有方法体,实现类可以不实现该方法,实现类对象可以调用该方法 - 静态方法:
static
修饰符修饰的方法,拥有方法体,因为可以多实现接口,可能会有静态方法冲突,所以不能通过接口实现类来调用接口静态方法,只能通过接口名点的形式调用
- 默认方法:
- jdk9 后新增:都拥有方法体了所以不能加
abstract
- 普通私有方法:
private
修饰,拥有方法体,实现类不能调用私有方法,解决多个普通方法之间调用问题 - 静态私有方法:
private static
修饰,拥有方法体,不能通过接口名点形式调用,解决多个静态方法之间调用问题
- 普通私有方法:
接口的多继承
- 若子接口多继承中有抽象方法冲突,等于没冲突
- 若子接口多继承中有默认方法冲突,子接口必须重写该默认方法
implements关键字
- 用于类实现接口,可以多实现,用逗号分隔
- 和继承类似,实现类拥有接口的一切,并且非抽象实现类必须实现接口的所有抽象方法
- 若实现类多实现中有抽象方法冲突,则实现类中重写一次即可
- 若实现类多实现中有默认方法冲突,则必须在实现类中重写该默认方法
- 继承父类方法和实现接口的默认方法冲突,优先继承父类的方法
Comments NOTHING