Java面向对象的一些关键知识
面向对象的四个特征
- 抽象: 提取出一类事物的共有属性和行为。
- 封装: 把抽象出的数据和行为封装在一起,供程序其他部分有权限地使用。
- 继承: 在已有结果基础上继续进行功能的扩充
- 多态: 同一类型的同一行为在不同情况下可以表现出不同的状态。
权限关键字
访问权限 | 本类 | 本包的类 | 子类 | 非子类的外包类 |
---|---|---|---|---|
public | 是 | 是 | 是 | 是 |
protected | 是 | 是 | 是 | 否 |
default | 是 | 是 | 否 | 否 |
private | 是 | 否 | 否 | 否 |
构造方法
- 方法名与类名相同,没有返回值类型
- 用于初始化对象属性
- 在使用new语句时被调用
- 不显示声明时,在编译时会为类自动加上一个无参的空构造方法
- 可通过重载实现多个接受不同参数的构造方法
- 显示声明时,将不再自动添加无参空构造方法
- 可在构造方法中使用
this(...)
调用其他构造方法,且该语句应放在方法的第一行。 - 保留无参空构造方法是一个好习惯(某些框架可依其通过反射创建对象)
- private修饰的构造方法可用于实现单例模式
值传递与引用传递
Java中只存在值传递。Java中包含两类数据类型:
- 基本类型
- byte
- short
- int
- long
- float
- double
- char
- boolean
- 引用类型,其值表示一个地址
- 类
- 接口
- 数组
继承
- Java中的继承是单继承
- 子类可以拥有父类非private的成员
- 子类可以定义自己的成员,对父类进行扩展
- 子类可以重写父类的方法(多态的实现)
- 子类不能继承父类的构造方法,但可以通过
super(...)
调用父类的构造方法 - 子类会调用父类的默认构造方法,但若默认构造方法不存在,则必须显式地通过
super
调用父类的构造方法,且该语句必须位于子类构造方法的第一行
对象初始化顺序
- 父类静态字段初始化、父类静态代码块(取决于二者声明的先后顺序)
- 子类静态字段初始化、子类静态代码块(取决于二者声明的先后顺序)
- 父类普通字段初始化
- 父类构造代码块
- 父类构造方法
- 子类普通字段初始化
- 子类构造代码块
- 子类构造方法
抽象类
- 定义类时使用abstract修饰
- 抽象类可以有构造方法、属性、方法实现
- 抽象类可以没有抽象方法,但存在抽象的方法一定要定义为抽象类
- 非抽象类继承抽象类时需要实现所有抽象方法
- 抽象类继承抽象类时可以不实现父类抽象方法
- 抽象类不能被实例化
- 抽象类不能用final修饰
接口
- 使用interface定义
- 接口可以继承其他多个接口
- 类可以实现多个接口
- 抽象类实现接口时可以不实现接口的方法
- 接口不能有构造方法
- 接口不能被实例化
接口中可以定义:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15interface Interface1 {
// 实际为 public static final int CONSTANT = 10;
int CONSTANT = 10;
// 实际为 public abstract void abstractMethod();
void abstractMethod();
default void defaultMethod() {
System.out.println("Default Implementation");
}
static void staticMethod() {
System.out.println("Static Method");
}
}常量
如CONSTANT的定义所示。可以被继承。接口中定义的所有属性默认用public static final修饰,这些关键字可以省略。当实现类中出现命名冲突时,可以使用
Interface1.CONSTANT
使用接口中的常量。抽象方法
如abstractMethod的定义所示。接口中定义的无实现方法默认使用public abstract修饰,这些关键字可以省略。实现接口时需要重写接口中的所有抽象方法。
默认实现方法(JDK 1.8及以后)
如defaultMethod的定义所示。默认实现方法存在方法体,在实现接口时可以不重写这些方法。但若实现类实现了多个接口,且这些接口中存在同名的默认实现方法,实现类中就必须重写重名的默认实现方法。
默认实现方法必须通过接口的实现类来调用,如
1
new Interface1Impl().defaultMethod();
静态方法(JDK1.8及以后)
如staticMethod的定义所示。不可以被继承。接口中的静态方法必须通过接口名访问,如
1
Interface1.staticMethod();
内部类
成员内部类
- 定义为外部类的一个成员
- 成员内部类对象依赖外部类对象而存在,在创建成员内部类对象前需要先创建其外部类对象
- 成员内部类中使用
this
访问自身的属性和方法,使用外部类名.this
访问其外部类中的属性和方法
示例:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42class Outer {
private int a1;
public Outer() {
a1 = 10;
System.out.println("Outer: "+a1);
}
public class Inner {
private int a1;
public Inner() {
a1=100;
// 访问外部类的属性
System.out.println("Inner.Outer: "+Outer.this.a1);
// 访问自身的属性
System.out.println("Inner: "+a1);
}
}
public void method() {
// 在外部类中实例化内部类对象
Inner inner = new Inner();
}
}
public class Main {
public static void main(String[] args) {
Outer outer = new Outer();
// 在外部类外部实例化内部类对象
Outer.Inner inner = outer.new Inner();
}
}
/*
Outer: 10
Inner.Outer: 10
Inner: 100
*/静态内部类
- 如同类的其他静态成员,在没有外部类对象时,可以使用
外部类名.内部类名
来使用静态内部类 - 静态内部类中无法访问外部类的非静态成员
示例:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42class Outer {
private static int a2;
public Outer() {
a2 = 11;
System.out.println("Outer: "+a2);
}
public static class StaticInner {
private int a2;
public StaticInner() {
a2=111;
// 访问外部类的静态属性
System.out.println("Inner.Outer: "+Outer.a2);
// 访问自身的属性
System.out.println("Inner: "+a2);
}
}
public void method() {
// 在外部类中实例化内部类对象
StaticInner inner = new StaticInner();
}
}
public class Main {
public static void main(String[] args) {
Outer outer = new Outer();
// 在外部类外部实例化内部类对象
Outer.StaticInner inner = new Outer.StaticInner();
}
}
/*
Outer: 11
Inner.Outer: 11
Inner: 111
*/- 如同类的其他静态成员,在没有外部类对象时,可以使用
局部内部类
- 定义在外部类的方法/代码块中
- 局部内部类不能有静态成员
- 局部内部类中可以使用
外部类名.this
访问其外部类中的属性和方法,但外部类不能访问局部内部类中定义的成员 - 局部内部类只能在定义域内被实例化
JDK 1.8以前,局部内部类对象不能使用该内部类所在方法的非
final
局部变量当调用方法时,局部变量如果没有用final修饰,其生命周期和方法的生命周期是一样的。方法被调用时会入栈,方法结束后即弹栈,这个局部变量也会消失。那么如果局部内部类对象还没有马上消失想用这个局部变量,显然已无法使用了。用final修饰的变量会在类加载的时候进入常量池,即使方法弹栈,常量池的常量还在,也就可以继续使用了。
JDK 1.8及以后编译器会自动为被局部内部类使用的局部变量加上final关键字修饰。
匿名内部类
示例:
1
2
3new 类名或接口名() {
// 重写或定义方法
}- 是局部内部类的延伸,所有局部内部类的限制都对其生效
- 一定跟在
new
后面
多态
- 多态分为方法的重写与重载和对象的多态性两部分
- 父类引用可以指向子类对象,自动转换(向上转型)
- 子类引用指向父类对象时需要强制转换(向下转型)
- 实现多态的三个必要条件:继承、重写、向上转型
- 继承: 在多态中需要存在有继承关系的父类和子类
- 重写: 子类对父类中某些方法进行重新定义,在调用这些方法时就会调用子类的方法。
- 向上转型: 父类的引用指向父类对象时,调用的是父类中定义的方法;指向子类对象时,调用的是子类中定义的方法。
- 将接口的引用指向不同的实现该接口的类实例可以实现基于接口的多态
关键字
this
- 指向当前对象的引用
- 调用属性:
this.属性名
,可用于解决成员变量名和局部变量名冲突 - 调用方法:
this.方法名(...)
- 调用构造方法:
this(...)
,只能用于构造方法中,且作为方法的第一条语句
super
- 指向父类的引用
- 调用父类的属性:
super.属性名
,可用于访问被子类隐藏的父类属性 - 调用父类的方法:
super.方法名(...)
- 调用父类的构造方法:
super(...)
,若父类的默认构造方法不存在,则在子类构造方法中必须使用这种方法显式地调用父类的构造方法,且作为方法的第一条语句
static
- 可修饰属性、方法、代码块,表示当前修饰的成员为静态的
- 通过
类名.属性名(或方法名)
调用 - 静态属性、静态方法属于类,而不是对象
- 静态属性按照定义顺序初始化
- 静态方法内不可使用
this
和super
关键字 - 静态方法内不可使用非静态成员,但非静态方法内部可以使用静态成员
- 静态代码块在类初次加载时被调用
final
- 修饰属性,表示声明一个常量
- 修饰方法,表示该方法无法被重写
- 修饰类,表示该类无法被继承
- 修饰方法参数,在方法内部不能修改参数的值
abstract
- 修饰类时表示该类为抽象类
细节见 <抽象类> - 修饰方法时表示该方法为抽象方法
- 只有声明,没有实现
- 抽象方法不能用private修饰,因为子类无法重写
- 抽象方法不能用static修饰
- 修饰类时表示该类为抽象类
instanceof
- 用于检查对象是否为某个类的实例
通常在把父类引用强制转为子类引用时使用,如:
1
2
3
4Object obj = ... ;// 从某个方法中获得一个Object类型引用
if (obj instanceof Integer) {
// do something
}