# java 面向对象

# 类定义对象创建

创建一个 cat 类

public class Cat {
    int age ;
    String name;
}

创建一个对象,关键字 new

public class Main {
    public static void main(String[] args) {
        Cat cat1 = new Cat();
        cat1.name = "Tom";
        cat1.age = 5;
        System.out.println("小猫的名字叫: " + cat1.name + " 它今年: " + cat1.age + "岁");
    }
}

# 方法的创建

其实方法就是个函数格式为;数据类型 方法名 {

} 数据类型可以是 int String 也可以是 void - 这意味着该方法不返回任何值

public class Ren {
    int age ;
    String name;
    void hello() {
        System.out.println("我叫" + name + " 我今年 " + age + " 岁了。");
    }
}
// 方法调用
public class Main {
    public static void main(String[] args) {
        Ren ren1 = new Ren();
        ren1.name = "Tom";
        ren1.age = 12;
        ren1.hello();
        Ren ren2 = new Ren();
        ren2.hello();
    }
}

# 方法的参数

return 方法结束返回结果 return 后的语句不在执行

public class Ren {
    int age ;
    String name;
    void hello(name) {
        System.out.println("我叫" + name + " 我今年 " + age + " 岁了。");
    }
}

# 方法重载

// 当前 sum 方法是 int 类型,如果参数传入的是小数就会报错
public class Ren {
    int age ;
    String name;
    void hello() {
        System.out.println("我叫" + name + " 我今年 " + age + " 岁了。");
    }
    int sum (int a , int b) {
        return a + b;
    }
}

image-20240621151246940

// 这时就要用到方法重载,当输入小数时就会自动匹配到 double 类型
public class Ren {
    int age ;
    String name;
    void hello() {
        System.out.println("我叫" + name + " 我今年 " + age + " 岁了。");
    }
    int sum (int a , int b) {
        return a + b;
    }
    double sum (double a , double b) {
        return a + b;
    }
}
// 通过重载就可以让同一个名字的方法接收多种类型的参数
public class Main {
    public static void main(String[] args) {
        Ren ren1 = new Ren();
        ren1.hello();
        System.out.println(ren1.sum(199.9,122.2));
    }
}

# this 关键字

# 构造方法

在 Java 中,每个类都有它的构造函数,当类的对象被创建时,该构造函数将被自动调用。构造函数类似于方法,但实际上它不是方法。

默认自带一个构造方法

构造方法在 new 时会自动执行

使用构造方法的好处

  1. 初始化对象: 构造方法可以确保所有对象在创建时都有一个有效的初始状态。
  2. 多态性: 可以通过重载构造方法(即定义多个构造方法)来支持不同的初始化方法。
  3. 封装性: 构造方法可以强制用户在创建对象时提供必要的信息,这有助于确保对象始终处于有效状态。
// 构造方法名称要和类名完全一致
public class Ren {
    int age = 18;
    String name = "未知";
    void hello() {
        System.out.println("我叫" + name + " 我今年 " + age + " 岁了。");
    }
    int sum (int a , int b) {
        return a + b;
    }
    double sum (double a , double b) {
        return a + b;
    }
    // 有参数构造方法
    public Ren(String name, int age) {
        this.age = age;
        this.name = name;
        
    }
    // 无参数构造方法
    Ren() {
    }
}
public class Main {
    public static void main(String[] args) {
        Ren ren1 = new Ren();// 匹配无参构造函数
//        ren1.hello();
//        System.out.println(ren1.sum(199.9,122.2));
        System.out.println(ren1.name);
        Ren ren2 = new Ren("Jane", 20);// 匹配有参构造函数
        System.out.println(ren2.name);
    }
}
// 输出
未知
Jane

有参构造方法

// 有参数构造方法
public Ren(String name, int age) {
    this.age = age;
    this.name = name;
    
}

无参构造方法

// 无参数构造方法
Ren() {
}

构造方法的多态性

当我们写一个有参的构造方法覆盖了默认的构造方法,如果 new 时不给参数会报错,可以通过重载构造方法构造一个没有参数的构造方法

这样在 new 时没有参数就会自动匹配无参构造方法,有参数时就会自动匹配有参构造方法

# 代码块

在类中添加代码块,他会在对象构造时执行一次,他会在变量初始化后执行在构造方法前面执行,变量初始化–> 代码块 —> 构造方法

public class Main {
    public static void main(String[] args) {
        Ren ren1 = new Ren();
//        ren1.hello();
//        System.out.println(ren1.sum(199.9,122.2));
        System.out.println(ren1.name);
        Ren ren2 = new Ren("Jane", 20);
        System.out.println(ren2.name);
    }
}
class Ren {
    int age = 18;
    String name = "未知";
    {
        System.out.println("我是构造代码块");
    }
    void hello() {
        System.out.println("我叫" + name + " 我今年 " + age + " 岁了。");
    }
    int sum (int a , int b) {
        return a + b;
    }
    double sum (double a , double b) {
        return a + b;
    }
    public Ren(String name, int age) {
        this.age = age;
        this.name = name;
    }
    public Ren() {
        System.out.println("我是默认构造方法");
    }
}

image-20240621180943931

# 静态变量静态方法

在 Java 中,静态方法和静态变量是与类相关的成员,而不是与类的实例相关的成员。它们由 static 关键字修饰。

# 静态变量

静态变量(或类变量)是属于类的,而不是属于类的某个实例的。所有实例共享同一个静态变量。这意味着,如果静态变量在一个实例中被修改,则所有实例中的该变量都会反映这一变化。

public class Cat {
    static String name;
}
public class Main {
    public static void main(String[] args) {
        Cat cat1 = new Cat();
        Cat cat2 = new Cat();
        cat1.name = "Tom";
        System.out.println(cat2.name);// 这里将会打印出 Tom,因为 cat1.name = "Tom" 改变的是类里面的而不是 cat1 里面的,所有其他实例中的 name 变量都会改变
    }
}
// 这样调用会提示警告,通过实例引用访问 static 成员 'Cat.name' ,因为静态变量可以直接用 Cat.name 调用而不需要通过实例调用
public class Main {
    public static void main(String[] args) {
        Cat.name = "Tom";
        System.out.println(Cat.name);
    }
}

静态方法

静态方法同理并不需要实例去调用因为他是属于类的而不是实例的

public class Cat {
    static String name;
    static void pao() {
        System.out.println("跑步");
    }
}
public class Main {
    public static void main(String[] args) {
        Cat.name = "Tom";
        System.out.println(Cat.name);
        Cat.pao();
    }
}
// 返回
Tom
跑步
// 在静态方法中不可以调用非静态变量和非静态方法,成员变量是某个具体对象拥有的属性,就像小明这个具体的人的名字才叫小明,而静态方法是类具有的,并不是具体对象的,肯定是没办法访问到的。同样的,在静态方法中,无法使用 this 关键字,因为 this 关键字代表的是当前的对象夺身。

image-20240621201340375

# 包和访问控制

包其实就是用来区分类位置的东西,也可以用来将我们的类进行分类(类似于 C++ 中的 namespace) 随着我们的程序不断变大,可能会创
建各种各样的类,他们可能会做不同的事情,那么这些类如果都放在一起的话,有点混乱,我们可以通过包的形式将这些类进行分类存
放。
包的命名规则同样是英文和数字的组合,最好是一个域名的格式,比如我们经常访问的 www.baidu.com, 后面的 paidu.com 就是域名
我们的包就可以命名为 com.baidu, 当然,各位小伙伴现在还没有自己的域名,所以说我们随便起一个名称就可以了。其中的・就是用
于分割的,对应多个文件夹,比如 com.test:

// 在包中的类要指明自己属于哪个包比如 Cat 类在 com.test 包里就要用到 package 关键字写明
package com.test;// 指明自己在 com.test 包中
public class Cat {
    public String name;
    public void pao() {
        System.out.println("跑步");
    }
}
// 包外面的要引用 Cat 类的话就要用 import 导入 com.test.Cat 也就是 com 文件夹下的 test 文件夹里的 Cat 类,也可以使用 * 表示导入这个包中的所有类
import com.test.Cat;
public class Main {
    public static void main(String[] args) {
        Cat cat1 = new Cat();
        Cat cat2 = new Cat();
        cat1.name = "Tom";
        System.out.println(cat1.name);
        Cat cat3 = new Cat();
        cat3.pao();
		System.out.println(Cat.a);
    }
}

访问权限控制

实际上 Jva 中是有访问权限控制的,就是我们个人的隐私的哑样,我不允许别人随便来查看我们的隐私,只有我们自己同意的情况下,才
能告诉别人我们的名字、年龄等隐私信息。
所以说 Jva 中引入了访问权限控制(可见性),我们可以为成员变量、成员方法、静态变量、静态方法甚至是类指定访问权限,不同的访
问权限,有着不同程度的访问限制:
・private~ 私有,标记为私有的内容无法被除当前类以外的任何位置访问。
什么都不写・默认,默认情况下,只能被类本身和同包中的其他类访问。
protected - 受保护,标记为受保护的内容可以能被类本身和同包中的其他类访问,也可以被子类访问(子类我们会在下一章介
绍)
public - 公共,标记为公共的内容,允许在任何地方被访问。
这四种访问权限,总结如下表:

当前类 同一个包下的类 不同包下的子类 不同包下的类
public 公共
protected 受保护 ×
默认 × ×
private 私有 × × ×

# 类的封装

封装的优点

  1. 提高代码的可维护性:通过隐藏内部实现细节,外部代码不会直接依赖于类的内部实现,从而可以自由地修改类的内部而不影响外部代码。
  2. 增强安全性:通过访问控制,可以防止外部代码随意修改对象的内部状态,确保对象状态的有效性和一致性。
  3. 提高代码的复用性:通过提供清晰的接口,类可以被重复使用而不需要了解其内部实现。

就相当于一台电视里面的电子原件电线啥的都给封装起来了,你看到的只有屏幕输出给你的画面至于怎么有的画面是不给你看的

Java 中的封装实现

定义类的属性为 private

这将确保属性不能被类的外部直接访问和修改。

提供公共的 gettersetter 方法

这些方法用于获取和设置属性的值,通常被定义为 public ,以便类的使用者可以通过这些方法访问和修改属性。

使用构造方法初始化对象

构造方法可以用来初始化对象的状态,确保对象在创建时处于有效的状态。

// 在此类中 age 和 name 都用了 private 私有其他任何人不可访问
package com.test;
// 类
public class Per {
    // 私有变量
    private int age;
    private String name;
    // 通过创建一个公共方法让外部可访问私有 age
    public int getAge() {
        return age;
    }
    // 通过创建一个公共方法让外部可操作私有 age
    public void setAge(int age){
        this.age = age;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
}
// 实例化对象时想查看 name 只能通过 getName 获取不可再用 per.name 因为在 Per 中 name 是私有的,只可以通过公共的 getAge 方法访问到
package com.test;
public class Main {
    public static void main(String[] args) {
        Per per = new Per();
        per.setName("Tom");
        per.setAge(100);
        System.out.println(per.getName());
        System.out.println(per.getAge());
    }
}
// 增加判断
package com.test;
// 类
public class Per {
    // 私有变量
    private int age;
    private String name;
    // 构造方法
    // 通过创建一个公共方法让外部可访问私有 age
    public int getAge() {
        return age;
    }
    public void setAge(int age){// 判断输入年龄是否合法
        if (age < 0 || age > 100 ){
            this.age = 3;
        }
        else{
            this.age = age;
        }
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
}

封装通过将对象的状态和行为结合在一起,并提供对外的访问接口,提高了代码的可维护性、安全性和复用性。在 Java 中,封装主要通过将属性定义为 private 并提供 publicgettersetter 方法来实现。

# 类继承

就是在一个大类中比如狗类,他的子类可以是导盲犬,警犬等,子类可以继承父类中的方法和变量除了 private 私有和默认,默认在同一个包下也可以继承,关键字 extends 格式为 public class 子类名 extends 父类名

package cn.dog;
//Dog 类
public class Dog {
    int age;
    String name = "小黑";
     String color;
     void action(){
         System.out.println(name+"在玩耍");
     }
}
package cn.dog;
// 警犬类继承了 Dog 类中的 name 变量和 action 方法,可以直接调用父类中的变量和方法
public class Policedog extends Dog {
    public void ming() {
        System.out.println("小狗的名字叫:" + name);
        action();
    }
}

父类中有构造函数时子类也要使用如果子类没有使用则会报错

image-20240622091057032

image-20240622091356101

// 父类
package cn.dog;
public class Dog {
    int age;
    String name = "小黑";
     String color;
     void action(){
         System.out.println(name+"在玩耍");
     }
     public Dog(int age, String name, String color) {// 父类构造函数
         this.age = age;
         this.name = name;
         this.color = color;
     }
}
// 子类,super 表示父类的构造函数
package cn.dog;
public class Policedog extends Dog {
    public Policedog(int age, String name, String color) {// 也必须要有
        super(age, name, color);
    }
    public void ming() {
        System.out.println("小狗的名字叫:" + name);
        action();
    }
}

子类可以定义与父类同名属性

// 子类里面的 ming 方法里面的 name 会变成离他最近的 name 也就是 Policedog 里面定义的 name,他没有值所以就会为空了
package cn.dog;
public class Policedog extends Dog {
    String name;
    public Policedog(int age, String name, String color) {
        super(age, name, color);
    }
    public void ming() {
        System.out.println("小狗的名字叫:" + name);
        action();
    }
}
// 返回小狗的名字叫:null
// 小红在玩耍
// 如果想要 name 属性是父类的 name 可以使用关键字 super.name 说明这个属性来自父类
package cn.dog;
public class Policedog extends Dog {
    String name;
    public Policedog(int age, String name, String color) {
        super(age, name, color);
    }
    public void ming() {
        System.out.println("小狗的名字叫:" + super.name);
        action();
    }
}
// 小狗的名字叫:小红
// 小红在玩耍

# Object 类

Object 类是顶级类所有类的祖宗,所有类继承于他

# 方法的重写

注意方法的重写跟重载是不一样的,重写是覆盖掉父类的方法让它变成我们想要的逻辑

比如 Object 类中的 equals 它是用来判断是不是一个对象

//equals 方法
public boolean equals(Object obj) {
    return (this == obj);
}
// 没有从写之前 dog1 和 dog2 这两个肯定不是一个对象所以返回 false
package cn.dog;
public class Main {
    public static void main(String[] args) {
        Policedog dog1 = new Policedog(20 , "小红","红色");
        Policedog dog2 = new Policedog(20 , "小红","红色");
        System.out.println(dog1.equals(dog2));
    }
}
// 方法重写
package cn.dog;
public class Dog {
    int age;
    String name;
     String color;
     void action(){
         System.out.println(name+"在玩耍");
     }
     public Dog(int age, String name, String color) {
         this.age = age;
         this.name = name;
         this.color = color;
     }
     @Override
     public boolean equals(Object obj) {
         if(obj == null)  return false;
         if(obj instanceof Dog dog) {
             return age == dog.age && name.equals(dog.name) && color.equals(dog.color);
         }
         return false;
     }
}
// 重写以后判断将以我们重写的 equals 逻辑比较
package cn.dog;
public class Main {
    public static void main(String[] args) {
        Policedog dog1 = new Policedog(20 , "小红","红色");
        Policedog dog2 = new Policedog(20 , "小红","红色");
        System.out.println(dog1.equals(dog2));
    }
}
返回true
// 除了 Object 类中的方法可以重写在其他父类的方法也都可以重写,父类的 ja 方法是加法
package cn.dog;
public class Dog {
    int age;
    String name;
     String color;
     void action(){
         System.out.println(name+"在玩耍");
     }
     public Dog(int age, String name, String color) {
         this.age = age;
         this.name = name;
         this.color = color;
     }
     @Override
     public boolean equals(Object obj) {
         if(obj == null)  return false;
         if(obj instanceof Dog dog) {
             return age == dog.age && name.equals(dog.name) && color.equals(dog.color);
         }
         return false;
     }
     public void ja(int a, int b){
         System.out.println(a+b);
     }
}
// 在子类中重写为乘法
package cn.dog;
public class Policedog extends Dog {
    String name;
    public Policedog(int age, String name, String color) {
        super(age, name, color);
    }
    public void ming() {
        System.out.println("小狗的名字叫:" + super.name);
        action();
    }
    @Deprecated
    public void ja(int a, int b){
        System.out.println(a*b);
    }
}

基于这种方法可以重写的特性,对于一个类定义的行为,不同的子类可以出现不同的行为,比如考试,学生考试可以得到 A,而工人去考试只能得到 D:

public class Person {
    ...
    public void exam(){
        System.out.println("我是考试方法");
    }
  
  	...
}
public class Student extends Person{
    ...
    @Override
    public void exam() {
        System.out.println("我是学生,我就是小镇做题家,拿个 A 轻轻松松");
    }
}
public class Worker extends Person{
    ...
    @Override
    public void exam() {
        System.out.println("我是工人,做题我并不擅长,只能得到 D");
    }
}

如果不想子类重写可以加上 final 关键字表示最终形态不可更改

public final void ja(int a, int b){
     System.out.println(a+b);

image-20240622102057100

final 关键字还可以用在类和属性上,用在类上表示不可继承

# 抽象类

在 Java 中,抽象类(abstract class)是一种特殊的类,它不能被实例化,但可以被其他类继承。抽象类通常用来定义一个通用的基类,为子类提供模板,并可以包含具体实现的部分。

# 抽象类的特点

  1. 抽象方法:抽象类可以包含抽象方法(没有实现的方法),这些方法必须在子类中实现。

  2. 具体方法:抽象类也可以包含具体方法(已经实现的方法),子类可以直接继承或重写这些方法。

  3. 构造方法:抽象类可以有构造方法,但不能直接实例化。

  4. 成员变量:抽象类可以包含成员变量,可以被子类继承和使用。

    使用抽象类的场景

    1. 统一接口:当你希望不同的子类有统一的接口(方法)时,可以使用抽象类。
    2. 代码重用:将共有的代码放在抽象类中,减少重复代码。
    3. 提供模板:为子类提供一个模板,让子类实现特定的功能。

    # 注意事项

    1. 抽象类不能被实例化。
    2. 子类必须实现所有的抽象方法,除非子类也是抽象类。
    3. 抽象类可以包含具体方法,子类可以选择继承或重写这些方法。
// 使用 abstract 关键字定义抽象类和抽象方法。例如:
// 抽象父类
package cn.dog;
public abstract class Cou {
    public abstract void eat();// 抽象方法
    public abstract void sleep();
    public abstract void run();
}
// 子类实现所有抽象方法
package cn.dog;
public class Cou2 extends Cou  {
    public void eat() {
        System.out.println("小狗在吃饭");
    }
    public void run() {
        System.out.println("小狗在跑");
    }
    public void sleep() {
        System.out.println("小狗在睡觉");
    }
}
//----------------------------------------
package cn.dog;
public class Cou3 extends Cou {
    public void sleep() {
        System.out.println("小猫在睡觉");
    }
    public void eat() {
        System.out.println("小猫在吃东西");
    }
    public void run() {
        System.out.println("小猫在跑");
    }
}

接口

定义一个接口

//play 接口 interface 定义接口
package cn.dog;
public interface play {
    void play();
}
// 引用接口 implements,和抽象类一样如果引用了这个接口就要实现里面的所有抽象方法不然报错
package cn.dog;
public class Cou3 extends Cou implements play {
    public void sleep() {
        System.out.println("小猫在睡觉");
    }
    public void eat() {
        System.out.println("小猫在吃东西");
    }
    public void run() {
        System.out.println("小猫在跑");
    }
    public void play() {
        System.out.println("小猫在玩耍");
    }
}

多接口

// 多个接口用逗号隔开
package cn.dog;
public class Cou3 extends Cou implements Play,Learn {
    public void sleep() {
        System.out.println("小猫在睡觉");
    }
    public void eat() {
        System.out.println("小猫在吃东西");
    }
    public void run() {
        System.out.println("小猫在跑");
    }
    public void play() {
        System.out.println("小猫在玩耍");
    }
    public void learn() {
        System.out.println("小猫在学习");
    }
}

接口也可继承

# 内部类

// 成员内部类可以调用私有的外部类中的属性
package cn.dog;
public class Wai {
    private String name = "admin";
    public void wai(){
        System.out.println("外部类的方法");
    }
    public class Nei {
        public void nei(){
            System.out.println("内部类的方法");
        }
        public void getName(){
            System.out.println(name);// 外部类私有属性
        }
    }
}
// 实例化内部类
package cn.dog;
public class Main {
    public static void main(String[] args) {
        Wai wai = new Wai();
        wai.wai();
        Wai.Nei nei = wai.new Nei();
        nei.nei();
        nei.getName();
    }
}
// 静态内部类,静态内部类不可调用外部类的私有属性方法
package cn.dog;
public class Wai {
    public void wai(){
        System.out.println("外部类的方法");
    }
    public static class Nei {
        public void nei(){
            System.out.println("内部类的方法");
        }
    }
}
//
package cn.dog;
public class Main {
    public static void main(String[] args) {
        Wai wai = new Wai();
        wai.wai();
        Wai.Nei nei = new Wai.Nei();
        nei.nei();
    }
}
// 一个 java 文件中只能有一个 public class 但是可以有多个 class