java基础02--面向对象

8 阅读8分钟

范围

Java面向对象知识,包括:封装继承多态、构造方法、this、super、final、static、抽象类、接口、内部类、枚举

封装继承多态

封装

把数据封装进类里面,操作核心是控制访问权限(用 private 保护属性,用 public 的 getter/setter 控制访问)

继承

子承父业,使用extends关键字,子类获得父亲的非private的属性和方法。
注意:单继承(一个儿子只有一个父亲)、方法重写@Override、子类构造得先调用父类构造
顺序:
静态>非静态,父类>子类,非构造方法>构造方法

多态

同一行为,不同表现。
前提:子承父业、子类重写父类方法、父类引用指向子类的对象

  1. 向上转型(生成多态)
    Animal animal = new Dog();
    父类引用 指向 子类对象,只能调用父类方法,用不了子类特有功能

  2. 向下转型(还原子类)

    Animal obj = new Dog(); // 不安全写法,直接强转容易崩 
    Dog d = (Dog)obj; // 安全写法 
    if(obj instanceof Dog){
     Dog d = (Dog)obj;
     d.wang(); // 调用狗狗独有方法
    }
    

示例

//---------------------------基类(封装+为继承设计)-------------------------------
public class OOTest {  
    //封装  
    private String name;  
    private Integer age;  
    //构造方法  
    public OOTest(String name, Integer age){  
        this.name = name;  
        this.age = age;  
    }  
    //构造方法-重载  
    public OOTest(String name){  
        this.name = name;  
        this.age = 18;  
    }  
    //公开gett/sett  
    public String getName() {return name;}  
    public void setName(String name) {this.name = name;}  
    public Integer getAge() {return age;}  
    public void setAge(Integer age) {this.age = age;}  
    //普通方法  
    public String makeSound(){return "齁齁齁齁齁齁";}  
    //静态方法  
    public static String makeSoundByname(String name){return name + "发出:齁齁齁齁";}  
}

//---------------------------子类(继承+重写)-------------------------------
public class OOTestsSon extends OOTest{  
    // 调用父类构造,完成 name、age 的初始化  
    public OOTestsSon(String name, Integer age) {  
        super(name, age);  
    }  
    public OOTestsSon(String name) {  
        super(name);  
    }  
    @Override  
    public String makeSound() {  
        return "喵喵喵";  
    }  
}
//---------------------------多态----------------------------------------
System.out.println("\n========== 多态调用 ==========");  
OOTest o1 = new OOTestsSon("小黑", 6);  
OOTest o2 = new OOTestsSon2("小黄", 7);  
// 调用同一个方法,实际执行的是子类重写后的版本  
System.out.println(o1.makeSound());  
System.out.println(o2.makeSound());  
//多态在集合中  
OOTest[] arr = {new OOTestsSon("小王", 8), new OOTestsSon2("小李", 9)};  
for (OOTest o : arr){  
    System.out.println(o.makeSound());  
    System.out.println(OOTest.makeSoundByname(o.getName()));  
}

调用顺序

========== 多态调用 ==========
【父类】静态代码块执行
【子类】静态代码块执行
【父类】普通代码块执行
【父类】双参构造方法执行
【子类】非静态代码块执行
【子类】双参构造方法执行
【子类】静态代码块执行
【父类】普通代码块执行
【父类】双参构造方法执行
【子类】非静态代码块执行
【子类】双参构造方法执行
喵喵喵
汪汪汪
--------------------------------
//静态方法只会执行一次
【父类】普通代码块执行
【父类】双参构造方法执行
【子类】非静态代码块执行
【子类】双参构造方法执行
【父类】普通代码块执行
【父类】双参构造方法执行
【子类】非静态代码块执行
【子类】双参构造方法执行
喵喵喵
小王发出:齁齁齁齁
汪汪汪
小李发出:齁齁齁齁

构造方法

注意点:

  1. 类加载时:先父类静态,再子类静态,各执行一次。
  2. 每次 new 时:先父类构造代码块 + 构造方法,再子类构造代码块 + 构造方法。
  3. super(args) 必须在子类构造第一行;如果不写,编译器自动插入 super()
  4. 构造代码块({} 直接写在类里)在构造方法之前执行,每 new 一个对象就执行一次。
  5. 若父类没有无参构造,子类必须显式 super(args),否则编译报错。

重载和this()例子

//这里 this()在一个构造里调用了重载的另一个构造,目的是复用初始化逻辑,避免重复代码
class Book {
    String title;
    double price;
    
    // 无参构造
    public Book() {
        this("未知书名", 0.0); // 调用本类有参构造,必须第一行
    }
    
    // 有参构造
    public Book(String title, double price) {
        this.title = title;
        this.price = price;
    }
}

static关键字

static就是把成员从「对象私有」提成「类全局公有」,免实例直接调用,值全局互通可修改
声明方法用static修饰,则该方法叫做静态方法,作用是跳过对象直接访问方法
静态方法只能访问静态变量和调用静态方法

静态代码块

静态代码块通常用来初始化一些静态变量,它会优先于 main() 方法执行
这一内容的作用主要是加载配置文件等等

final关键字

固定常量,final可以修饰变量、对象、方法、类,作用都类似于将其变为“最终状态不可修改状态

final变量

final修饰的变量不可以修改,并且要在声明的时候完成初始化
例如:final修饰的person对象,person不可修改,但是person.age可以修改
final+static一起修饰的叫做常量,即不可修改且唯一

final方法

不可被重写

final类

不可被继承,但是final类的对象是可以改变的

this和super

this指向对象本身,super指向爸爸那部分

this关键字

  1. 作为引用变量,指向当前对象
    举例:在一个类中声明了一个变量,同时定义了一个方法,此方法的内容是将变量赋值为传入的参数,同时传入参数名和成员变量名相同,如果没有使用this指明,java就不知道此变量是不是类的成员变量
    结果:null
  2. 调用当前类的方法
    在当前类的某一个方法中,使用this.方法名来调用另一个该类中的方法
  3. 调用当前类的构造方法
    在一个构造方法中调用另一个参数不同的构造方法,但是this语句必须写在第一句
  4. 作为参数传递
    把当前对象自己传给另一个方法(可以是外部工具类、其他对象的方法),让它们操作当前对象
  5. 作为参数在构造方法中传递
    this 指向的是已经开好内存的半成品对象,不是完整成品,所以构造执行中,拿this传参、调用本类普通方法全都没问题。
  6. 作为方法返回值

super关键字

  1. 指向父对象
  2. 调用父方法
  3. super()可以调用父类的构造方法

instanceof关键字

  1. 语法

    对象 instanceof 类名
    
  2. 规则

    1. 本类对象 → true
    2. 子类对象 判断父类 → true
    3. 父类对象 判断子类 → false
    4. null instanceof 任意 → false
  3. 例子

    class Animal{} 
    class Dog extends Animal{} 
    Dog d = new Dog(); 
    Animal a = new Animal(); 
    System.out.println(d instanceof Dog); // true 
    System.out.println(d instanceof Animal); // true 
    System.out.println(a instanceof Dog); // false
    

抽象类 abstract

抽象类命名要以Abstract或Base开头
特性:不能被实例化,并且子类必须实现它的抽象方法
主要作用:在于继承时的扩展,比如在很多子类中都有但是内容却不一样的方法

接口 interface

可以实现:定义变量、定义抽象方法、定义默认方法default、定义静态方法
限制:

  1. 不允许直接实例化
  2. 接口可以为空
  3. 定义接口不能使用final
  4. 接口的抽象方法不能为private、protected、final
  5. 加快的变量为隐式常量,值不能改变
    作用:实现多继承
    抽象和接口区别:抽象是一个类,接口可以是一个动作或变量

内部类

内部类就是定义在另一个类内部的类,有四种形态:
成员内部类、局部内部类、匿名内部类和静态内部类

成员

最常见、最简单。内部类可以任意调用或访问外部类的成员,而外部类想要访问内部类,需要先声明一个内部类的对象
相当于内部类和外部类完全绑定

局部

定义在一个方法或者一个作用域里面的类,所以生命周期仅限于作用域,不能被权限修饰符修饰,访问权限与成员内部类一样,但访问局部变量时只能访问final
没怎么见过

匿名

最常用,主要作用用来继承其他类或者实现接口,并不需要增加额外的方法,方便对继承的方法进行实现或者重写
lambda大量代替

// 以前:匿名类实现 Runnable
new Thread(new Runnable() {
    public void run() { System.out.println("run"); }
}).start();

// 现在:Lambda 表达式
new Thread(() -> System.out.println("run")).start();

// 必须匿名类:抽象类无法用 Lambda
Payment p = new Payment("001", 200) {
    public boolean pay() { return true; }
    protected String getMethod() { return "自定义"; }
};

静态

成员内部类+static,访问时只能访问静态变量和方法

总结:

内部类是否需要访问外部实例成员?
├─ 不需要 → 静态内部类
└─ 需要
├─ 只在某个方法里用到 → 可考虑局部内部类(很少用)
├─ 被多处使用,且和外部状态强相关 → 成员内部类
└─ 仅需一次性创建 → 匿名内部类(或 Lambda)

枚举 enum

一种特殊的类,是单例的最佳实现

  1. 命名规范

    • 枚举类名使用大驼峰命名法(PascalCase)
    • 枚举常量使用全大写,单词间用下划线分隔(UPPER_SNAKE_CASE)
    • 枚举类通常应该是公共的(public)
  2. 使用建议

    • 当需要表示一组固定的常量时,应优先使用枚举
    • 为枚举添加有意义的方法和属性,而不是仅仅作为常量集合
    • 使用枚举的 valueOf() 方法时要注意处理异常
    • 在 switch 语句中使用枚举时,建议处理所有可能的枚举值

示例

@Getter
public enum ErrorCode {
	SUCCESS(0, "操作成功"),
    PARAM_ERROR(400, "参数错误"),
    UNAUTHORIZED(401, "未授权"),
    FORBIDDEN(403, "禁止访问"),
    NOT_FOUND(404, "资源不存在"),
    INTERNAL_ERROR(500, "服务器内部错误");
    ErrorCode(int code, String message) { 
	    this.code = code; this.message = message; 
    }

    private final int code;
    private final String message;
	
	public static ErrorCode fromCode(int code){
		for(ErrorCode e : values()){
			if(e.code == code )return e;
		}
		throw new IllegalArgumentException("未知:"+code);
	}
}