七、面向对象(高级)
七、面向对象(高级)
Pupper一、类变量和类方法
1. 类变量
类变量(静态变量/静态属性):是该类所有对象共享的变量,该类所有的对象都可以对齐进行修改或引用。
语法:static : 表示静态
1 | 访问修饰符 static 数据类型 变量名;(推荐该写法) |
引用语法:
1 | 类名.类变量名;(推荐使用) |
:::note
- 类变量是随着类的加载而创建的,即使没有创建对象,也可以访问;
- 类变量的访问,也要遵守访问修饰符的访问权限;
- 没有加 static 则表示为实例变量,不共享,加上则表示为 类变量,是共享的;
- 类变量的生命周期时随着类的加载而加载,随着类的消亡而消亡;
:::
2.类方法
类方法,也叫静态方法;
语法:
1 | 访问修饰符 static 返回数据类型 方法名(){};(推荐写法) |
调用:
1 | 类名.类方法名(); |
::: warning
- 非静态方法不能通过类调用,只能通过对象调用;
- 静态方法既可以通过类调用,也可以通过类调用;
- 类方法中不允许使用和对象有关的关键字
- 如: this,super
- 静态方法中,只能访问静态变量或静态方法
- 普通成员方法 都可以访问;
:::
- 普通成员方法 都可以访问;
静态方法,只能访问静态的成员;
普通方法,静态与非静态的成员都可以访问;
使用场景:
::: tip
- 用来编写不涉及对象的方法,比如:工具类
:::
二、代码块
代码块(初始化块):属于类中的成员,类似于方法,将逻辑语句封装在方法体中,通过{}包围起来;
与方法不同,没有方法名,没有返回,没有参数,只有方法体,不能通过对象或类显式调用,在加载类或创建对象时隐式调用
语法:
1 | [修饰符]{代码}; |
:::warning
- 修饰符 可选,要写只能写 static
- 没有修饰符的叫普通代码块,有修饰符的叫静态代码块
- “;” 可以省略
- 代码块的调用顺序优先于构造器
:::
:::note
类什么时候加载:
- 创建实例对象时
- 创建子类对象实例时,父类也会加载(父类先加载,子类后加载);
- 使用类的静态成员时
:::
:::tip
细节:
- 静态代码块只会执行一次,普通代码块 每创建一个对象,就会执行一次
- 如果只使用静态成员时,普通成员不会执行
- 创建对象时,类 调用顺序:
- 调用静态代码块和静态属性初始化(优先级一样,多个时按定义顺序调用)
- 调用普通代码块和普通属性的初始化(同上)
- 调用构造器方法
- 构造器的前面隐含了 super()和 调用普通代码块,静态相关的代码块和属性初始化在类加载时就执行完成了,因此是优先于 构造器和普通代码块执行的。
- 有继承关系的子类对象在创建时的调用顺序:
- 父类静态代码块、静态属性(优先级一样,按定义顺序执行)
- 子类静态代码块、静态属性(优先级一样,按定义顺序执行)
- 父类普通代码块、普通属性(优先级一样,按定义顺序执行)
- 父类的构造方法
- 子类普通代码块、普通属性(优先级一样,按定义顺序执行)
- 子类的构造方法
- 静态代码块只能调用静态成员,普通代码块可以调用任意成员
:::
1 | public class StaticDemo01 { |
1 | public class StaticDemo02 { |
三、单例设计模式
单例模式: 采取一定的方法,使某个类只存在一个对象实例,并且该类只提供一个获取对象实例的方法。
单例模式:
①。饿汉式: 类在加载时直接创建一个对象,不管后续有没有使用;
②。懒汉式: 只有当使用时,类才会加载一个对象;
:::note
饿汉式 和 懒汉式 设计模式的实现:
- 构造器私有化(防止直接 new 对象)
- 类的内部创建对象
- 向外暴露一个静态的公共方法
:::
1 | // 单例模式 - 饿汉式 案例 |
1 | // 单例模式 - 懒汉式 |
四、final 关键字
final : 表示最后的、最终的
final 可以用于修饰类、属性、方法、局部变量等
:::tip
使用场景:
- 当不希望类被继承时,可以使用 final 修饰
- 当不希望父类的某个方法被子类覆盖、重写时,可以使用 final 关键字修饰
- 当不希望某个属性的值被修改时,可以使用 final 修饰
- 当不希望某个是不变量被修改时,可以使用 final 修饰
:::
:::note
注意事项:
- final 修饰的属性又叫常量,一般用 全大写字母 和 下划线 来命名
- NUM_TEST
- final 修饰的属性在定义时,必须赋初始值,并且以后不能再修改
- 定义时:如:public final double TAX_RATE = 0.08;
- 在构造器中赋值;
- 在代码块中赋值;
- 如果 final 修饰的是静态属性,则初始化的位置只能
- 在定义时
- 在静态代码块中
- 不能在构造器中赋值
- final 类不能继承,但是可以实例化对象
- 如果类不是 final 类,但是含有 final 方法,则该方法虽然不能重写,但是可以被继承
- 如果一个类 被 final 修饰了,那么他的方法就没有必要再用 final 修饰
- final 不能修饰构造器
:::
1 | // final 关键字 |
五、抽象类(abstract)
抽象类: 当类中的某些方法不确定时,就可以使用 abstract 关键字将方法修饰为抽象方法,用 abstract 修饰类成为抽象类,一般都会由子类继承,由子类来实现抽象方法。
语法:
1 | // 抽象类及抽象方法 |
::: tip
注意事项:
- 抽象类和抽象方法必须使用 abstract 修饰;
- 抽象方法没有方法体;
- 抽象类一般都会被继承,由子类重写抽象方法;
- 抽象类不能实例化;
- 抽象类不一定有抽象方法,但是有抽象方法,则必须是抽象类;
- abstract 只能修饰 类 或 方法;
- 如果继承了抽象类,则必须实现抽象方法,或者自己也申明为抽象类
- 抽象方法不能用 final、static、private 来修饰,与抽象方法规则相违背;
:::
1 | // 抽象类 |
1 | // 抽象 - 模板模式 |
六、接口
接口: 就是给出一些没有实现的方法封装到一起,到某个类要使用的时候,再根据具体情况把这些方法写出来
使用 interface 来定义接口
使用 implement 来实现接口
语法:
1 | interface 接口名{ |
::: warning
注意事项:
- 接口不能被实例化;
- 接口中所有的方法都是 public 方法,接口中的抽象方法 可以不用 abstract 修饰
- 一个普通类实现接口,就必须将该接口的所有方法都实现
- 抽象类实现接口,可以不用实现接口的方法
- 一个类同时可以实现多个接口
- 接口中的属性,只能是 final 的,而且是 public static final 修饰符
- 如: int a =1; 实际上是 public static final int a = 1;(必须初始化)
- 接口中属性的访问形式: 接口名.属性名
- 一个接口不能继承其他的类,但是可以继承多个接口
- 如: interface A extends B,C{}
- 接口的修饰符只能是 public 和 默认,这点和 类的修饰符一样
:::
当子类继承类父类,就自动拥有父类的功能,如果子类需要扩展功能,则可以通过实现接口的方式扩展
实现接口 是对 java 单继承的一个补充
:::note
继承 与 接口实现的区别:
- 继承时,会自动拥有父类的所有功能;
- 接口是对 单继承的一种补充,用于扩展子类功能
- 继承主要解决代码的复用性和可维护性
- 接口主要用于设计各种规范,让其他类去实现这些方法
- 接口相较于继承更加灵活
:::
1 | // 接口与继承 |
:::tip
接口的多态特性:
- 多态参数(一个接口有多个方法实现时,通过参数判断实现对象)
- 多态数组
- 多态传递(一个接口继承了另一个接口)
:::
1 | // 接口 - 多态数组 |
七、内部类
内部类 : 一个类的内部又完整的嵌套了另一个类结构,
类的五大成员: 属性、代码块、构造器、方法、内部类
语法:
1 | class Outer{ // 外部类 |
:::note
内部类的分类:
- 根据定义在外部类的位置:
- 局部内部类(有类名)
- 匿名内部类(没有类名)
- 根据定义在外部类的成员位置:
- 成员内部类(没有 static 修饰)
- 静态内部类(有 static 修饰)
:::
1. 局部内部类
:::tip
说明:
- 通常定义在方法中;
- 可以直接访问外部类的所有成员,包括私有的属性或方法;
- 不能添加访问修饰符,但是可以使用 final 修饰符
- 作用域:只在定义他的方法或代码块中使用``
- 局部内部类访问外部类时,可以直接访问
- 外部类访问局部内部类时,需要先创建对象,在进行访问
- 外部其他类不能访问局部内部类
- 如果外部类和内部类的成员重名,则默认采取就近原则,
- 如果要访问外部类成员,可以使用 外部类.this.成员
:::
- 如果要访问外部类成员,可以使用 外部类.this.成员
1 | // 局部内部类 |
2.匿名内部类
语法:
1 | new 类或接口(形参){ |
:::danger
注意事项:
- 调用匿名内部类的方法:
new A(){}.cry();
A a = new A(){}; a.cry();
- 可以直接访问外部类的所有成员,包括私有属性
- 不能添加访问修饰符,因为它就是一个局部变量
- 作用域:仅定义在他的方法或代码块中
- 外部其他类,不能访问匿名内部类
- 外部类和内部类的成员重名时,默认遵守就近原则;
:::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// 匿名内部类 - 案例 1
public class LocalClass2 {
public static void main(String[] args) {
Cellphone cellphone = new Cellphone();
cellphone.alarmclock(new Bell() {
public void ring() {
System.out.println("懒猪起床了");
}
});
cellphone.alarmclock(new Bell() {
public void ring() {
System.out.println("小伙伴们上课了");
}
});
}
}
interface Bell{
void ring();
}
class Cellphone{
public void alarmclock(Bell bell){
bell.ring();
}
}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
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59// 匿名内部类 - 案例 2
public class AnonymousInnerClass {
public static void main(String[] args) {
Other01 other01 = new Other01();
other01.method1();
}
}
class Other01{
public void method1(){
// 基于 接口 的匿名内部类
// 匿名内部类 只能被调用一次,但是对象可以调用多次(tiger 为对象)
IA tiger = new IA() {
public void cry() {
System.out.println("老虎叫~~~");
}
};
// 匿名内部类调用
tiger.cry();
// 基于 类 的匿名内部类
// 不加 {},则为普通的创建对象,加上 {} 则表示匿名内部类
Father father = new Father("张三"){
public void test() {
System.out.println("匿名内部类重写类 test 方法");
}
};
father.test();
// 基于 抽象类 的匿名内部函数
Animal animal = new Animal(){
void eat() {
System.out.println("实现抽象类的抽象方法");
}
};
animal.eat();
}
}
interface IA{
void cry();
}
class Father{
String name;
public Father(String name){
this.name = name;
};
public void test(){
System.out.println(name);
};
}
abstract class Animal{
abstract void eat();
}
3.成员内部类
成员内部类定义在外部类的成员位置,并且没有 static 修饰
:::tip
- 可以直接访问外部类的所有成员,包括私有成员
- 可以添加任意访问修饰符(public、protected、默认、private),他的定位时成员;
- 作用域:和外部类的成员方法一样
- 成员内部类 可以直接访问 外部类
- 外部类 访问 成员内部类时,需要先创建内部类对象,再访问
- 外部其他类 可以访问 内部成员类
- 如果外部类和内部类的成员重名,则默认遵守就近原则
- 如果要访问外部类成员,则 通过(外部类名.this.成员名)访问
:::1
2
3
4
5
6
7
8
9
10
11// 外部其他类访问 成员内部类的方法
// 方法 1
Outer1 outer1 = new Outer1();
Innter1 innter1 = outer1.new Innter1();
// 方式 2
Innter2 innter2 = new Outer2().new Innter2();
// 方式 3(使用一个方法来获取)
Innter3 innter3d = new Outer3().getInnter3();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// 成员内部类 - 案例
public class LocalClass3 {
public static void main(String[] args) {
Outer1 outer1 = new Outer1();
outer1.t1();
// 外部其他类 访问成员内部类
// 通过外部类对象,创建成员内部类实例对象
Outer1.Inner1 inner1 = outer1.new Inner1();
inner1.say();
// 通过 成员方法调用 成员内部类
Outer1.Inner1 inner1 = outer1.getInner1();
inner1.say();
}
}
class Outer1 {
private int n1 = 10;
private String name = "张三";
// 成员内部类
// 成员内部类可以使用任何 访问修饰符
public class Inner1{
public void say(){
// 内部类 可以直接访问外部类成员方法或属性,包括私有属性
System.out.printf("n1= %d,name= %s",n1,name);
}
}
public void t1(){
// 外部类访问成员内部类时,需要先创建内部类对象,再进行访问
Inner1 inner1 = new Inner1();
inner1.say();
}
// 编写一个方法,返回 成员内部类的实例对象
public Inner1 getInner1(){
return new Inner1();
}
}
- 如果要访问外部类成员,则 通过(外部类名.this.成员名)访问
4.静态内部类
静态内部类定义在 外部类的成员位置, 并且有 static 修饰符
:::tip
- 可以直接访问外部类的所有静态成员,包括私有的,但不能直接访问非静态成员
- 可以添加任意访问修饰符,因为它定位为成员
- 作用域: 和其他成员相同
- 静态内部类可以直接访问外部类的静态成员
- 外部类访问静态内部类时,需要先创建对象,再进行访问
- 外部其他类 可以访问 静态内部类
- 如果外部类和静态内部类的成员重名,则默认遵守就近原则
- 如果要访问外部类成员,则 通过(外部类名.成员名)访问
:::
- 如果要访问外部类成员,则 通过(外部类名.成员名)访问
1 | // 静态内部类 - 案例 |