Java笔记——面向对象

1 类与对象

1.1 类与对象的关系

属性(成员变量):类似定义变量,但不在方法内
行为(成员方法):类似定义方法,但不含static

1.2 类的创建

对象名.成员变量
对象名.成员方法

如图片所示,在创建类的时候,首先在方法区加载TestDemo2类和Car类,以及其中的成员变量和成员方法

而后执行Car car = new Car(),new Car()创建的对象实例在堆内存中,在栈内存声明的对象变量,car是一个指针,指向堆内存中的对象实例,实际上是一个指向堆内存对象的引用。在堆内存的生命周期当中,只要还有指向其对象的引用,堆内存中的对象就会一直存在

栈内存的局部变量的生命周期只存在于方法内部,会在执行完对应方法后被回收,而堆内存中的变量对象不会自动释放,直到确定没有任何引用指向它才会被垃圾收集器回收

2 构造方法

  • 当类没有提供任何构造方法,那么会有一个创建一个无参构造方法,如果已存在有参构造方法,则不会自动创建无参构造
  • 构造方法可以重载

3 关键字

3.1 private

  • private是一个关键字,也是一个权限修饰符,可以修饰成员,被private修饰的成员只能在本类中才能访问
  • private通常用来修饰成员变量,不允许外部直接访问,而是提供对应的set/get方法来间接访问成员变量,这样可以将内部成员隐藏起来,提高代码的安全性、保障数据不受外部损害,并提高代码的内聚性

3.2 this

  • this是一个关键字,指向调用的对象,在构造方法中this()必须在代码的第一行,因为当创建一个新的对象时,jvm必须知道执行哪个构造器,不过不放在第一行,jvm就无法确定是否存在其他需要执行的构造函数,且构造函数只能被构造函数调用,因为对象实例只会初始化一次
public class ThisDemo {
    public static void main(String[] args) {
        new ThisDemoClass(5,6);
    }
}
class ThisDemoClass{
    int a = 1;
    int b = 2;
    public ThisDemoClass(int a) {
        System.out.println("a = " + a);

    }

    public ThisDemoClass(int a, int b) {
        this(3); //this()方法必须在第一行
        this.a = a;
        this.b = b;
        System.out.print("a = " + a +",b = "+ b);
    }
}

运行截图

this.成员变量

private String name;
private int age;
public Person(String name,int age){
    this.name = name;
    this.age = age;
}

public String getName(){
    return name;
}

public int setAge(int age){
    this.age = age;
}

this.参数 //调用当前类的构造方法
this.成员方法

public void eat(){
        System.out.println(name + "要吃饭");
    }

    public void sleep(){
        // 成员方法(对象方法) 必须使用对象来调用
        this.eat();
        System.out.println(this);
        System.out.println("人要睡觉");
    }

3.3 super

  • super()用于调用父类的构造方法,所有构造方法第一行默认会有一个super(),但super()和this()
  • super.成员方法() super.成员变量,用于访问父类的方法和成员变量
  • super() 必须在构造方法的第一行,因为构造函数的目的是初始化对象,子类继承父类,并可能会访问父类的成员变量或方法,所以要在子类可以访问父类之前完成父类对象的初始化
    public class super1 {
    public static void main(String[] args) {
        Dog dog = new Dog();
        dog.eat("狗狗");
    }
    }
    class Pet{
    String name;
    public Pet(String name) {
        this.name = name;
        System.out.println(name);
    }
    public void eat(String name){
        System.out.println(name + "在吃饭");
    }
    }
    class Dog extends Pet{
    public Dog() {
        //默认调用父类的无参构造
        super("狗");
        System.out.println("子类调用父类");
    }
    }

    运行截图

    4 代码块

    4.1 构造代码块

  • 在创建对象时,构造代码块先于构造方法运行

    public class CodeBlockDemo {
    public static void main(String[] args) {
        new CodeBlockDemo2();
    }
    }
    class CodeBlockDemo2{
    {
        System.out.println("构造代码块");
    }
    public CodeBlockDemo2(String a) {
        System.out.println(a);
    }
    
    public CodeBlockDemo2() {
        this("另一个构造函数");
        System.out.println("对象默认构造函数");
    }
    }

    运行截图

    4.2 局部代码块

    存在于方法中的代码块,由于最先运行并且其中的局部变量等运行完毕后立即销毁,所以提高了栈内存的利用率

    5 多态

    5.1 继承

  • 子类可以通过继承获得父类的所有方法和属性,优点是提高代码复用性,但默认下只有同包子类可以访问,子类不能访问private修饰的方法和属性
  • 类与类是单继承关系,整体继承关系呈现树状结构,类之间可以连环继承,但一个类只能继承一个类
    public class JiCheng {
    public static void main(String[] args) {
        Dog dog = new Dog();
        dog.color = "棕黄";
        dog.name = "大黄狗";
        System.out.print(dog.color + "色的" + dog.name + "在");
        dog.shout();
        dog.eat();
    }
    }
    class Pet{
    String name;
    String color;
    public void shout(){
        System.out.println("叫");
    }
    }
    class Dog extends Pet{
    public void eat(){
        System.out.println("狗在啃骨头");
    }
    }

    运行截图

5.2 重写

当子类继承父类后,可以实现不同于父类的功能,对于具有相同签名(同名)的方法,子类可以通过重写来改变父类的行为,但重写的方法名、返回的参数类型和传参列表都必须与父类的方法相同

public class OverrideDemo {
    public static void main(String[] args) {
        OverrideDemo2 overrideDemo2 = new OverrideDemo2();
        overrideDemo2.out();
    }

    void out(){
        System.out.println("这是父类的内容");
    }
}
class OverrideDemo2 extends OverrideDemo{
    @Override
    void out() {
//        super.out(); //如果包括了super(),则会先执行父类方法,再执行子类的重写内容
        System.out.println("这是子类重写的内容");
    }
}

运行截图

5.3 对象数组

public class ObjectArrayDemo {
    public static void main(String[] args) {
        ObjectArrayDemoCat cat = new ObjectArrayDemoCat("猫",5);
        ObjectArrayDemoDog dog = new ObjectArrayDemoDog("狗",8);
        ObjectArrayDemoPet[] petArr = new ObjectArrayDemoPet[3];
        petArr[0] = cat;
        petArr[1] = dog;
        for (ObjectArrayDemoPet pet : petArr){
            if (pet == null){
                break;
            }
            System.out.println(pet.getName() + "的年龄为" + pet.getAge());
        }
    }
}
class ObjectArrayDemoPet {
    String name;
    int age;

    public String getName() {
        return name;
    }

    public int getAge() {
        return age;
    }
    public ObjectArrayDemoPet(String name, int age) {
        this.name = name;
        this.age = age;
    }
}

class ObjectArrayDemoDog extends ObjectArrayDemoPet{

    public ObjectArrayDemoDog(String name, int age) {
        super(name, age);
    }
}

class ObjectArrayDemoCat extends ObjectArrayDemoPet{

    public ObjectArrayDemoCat(String name, int age) {
        super(name, age);
    }
}

运行截图

6 权限修饰符

本类中 子类中 同包类中 其他类中
public 可以 可以 可以 可以
protected 可以 可以 可以 不可以
默认 可以 同包的子类中 可以 不可以
private 可以 不可以 不可以 不可以

7 接口

7.1 抽象类

7.1.1 基本概念

  • 抽象类不能实例化,即无法创建对象

  • 抽象类可以使用抽象方法,但只能声明方法,不能实现功能,该方法必须由子类实现,详情代码如下:

public class AbstractDemo {
    public static void main(String[] args) {
        Dog dog = new Dog();
        dog.shout();
        dog.sleep();
    }
}

abstract class AbstractDemoPet{
    abstract void shout();

    void sleep(){
        System.out.println("睡觉了");
    }
}

class Dog extends AbstractDemoPet{

    @Override
    void shout() {
        System.out.println("汪汪汪");
    }
}

运行截图

  • 抽象类也可以像普通类一样拥有普通成员变量和方法,也可以使用构造函数完成初始化

    7.1.2 抽象类和普通类的区别

  • 抽象类更适用于搭建功能的基础框,用于定义一组共有特征的功能,确保子类都能实现特定的功能。而普通类更适用于创建对象实例,用于简单明了、不需要被继承或扩展的功能实现

    7.2 接口

    7.2.1 接口继承关系

  • 与类不同,接口与接口是多继承关系,一个接口可以继承多个接口
 public interface AInterface {
 void a();
 }
 interface BInterface  {
 void c();
 }
 Java
interface CInterface extends AInterface, BInterface {
void b();
 }

7.2.2 接口实现(implments)

  • 类与接口是多实现关系,一个类可以实现多个接口,接口与抽象类似,不能用于实例化对象,必须作为高层接口被子类实现功能,具体代码格式接近于继承
public interface Plugin {
    void launch();
    void run();
    void ruin();
}

package q1;

public class Software{

    void launch(Plugin plugin) {
        plugin.launch();
    }

    void run(Plugin plugin) {
        plugin.run();
    }

    void uninstall(Plugin plugin) {
        plugin.ruin();
    }
}
class QQMusic extends Software implements Plugin{

    @Override
    public void launch() {
        System.out.println("启动");
    }
    @Override
    public void ruin() {
        System.out.println("结束");
    }
}
}

public class Test {
    public static void main(String[] args) {
        Software software = new Software();
        QQMusic qqMusic = new QQMusic();
        qqMusic.launch();
        qqMusic.ruin();
    }
}

运行截图

7.2.3 接口与抽象类的区别

抽象类 接口
属性 无限制 public静态常量
构造方法 可选
普通方法 可以有普通具体方法 必须是public抽象方法
子类 单继承 多继承(接口对接口)

7.3 @FunctionalInterface

@FunctionalInterface

  • @FunctionalInterface 注解表示当前接口为函数式接口
  • @FunctionalInterface 注解不是必须的
  • @FunctionalInterface 表示当前接口有且仅有一个抽象方法,同时意味着可以使用lambda表达式进行简化表达

7.4 Lambda

7.4.1 概念

  • Lambda可以根据任意一个类/接口创建一个匿名内部类,用于替代接口的匿名内部类
  • 接口匿名内部类等于接口实现类

    7.4.2 语法

  • 基本语法1:

    (形参类型 变量1…形参类型 变量n)->{
    //⽅法体 核⼼逻辑
    }

  • 简化语法2:(⽅法引⽤)
    引⽤/对象::⽅法名;
    类名::⽅法名;

interface LambdaDemoInterface2 {
    int operate(int a, int b);
}

public class LambdaDemo2 {
    public static void main(String[] args) {
        LambdaDemoInterface2 lambdaDemoInterface2 = (a, b) -> a * b;
        //等价于
//        LambdaDemoInterface2 lambdaDemoInterface3 = new LambdaDemoInterface2() {
//            @Override
//            public int operate(int a, int b) {
//                return a * b;
//            }
//        };

        int result = lambdaDemoInterface2.operate(10, 20);
        System.out.println("结果: " + result);

    }
}

运行截图

8 枚举

8.1基本知识

  • 这部分知识点比较碎,我在代码中做出注释

    public enum EnumDemo {
    /**
     * enum枚举类的第一行代码必须是枚举常量(枚举对象)
     * 通过反编译class文件可以发现
     * 'SUNDAY,MONDAY,TUESDAY;'这一行实际上等于:
     * private static final EnumDemo SUNDAY = new EnumDemo
     * private static final EnumDemo MONDAY = new EnumDemo
     * private static final EnumDemo TUESDAY = new EnumDemo
     * 所以他们都是被确定过的,无法被更改
     */
    SUNDAY,MONDAY,TUESDAY;
    
    private String name;
    
    public String getName() {
        return name;
    }
    
    public void setName(String name) {
        this.name = name;
    }
    public static void main(String[] args) {
        //😅千万不要这么写,枚举类是不可被实例化的
        //EnumDemo enumDemo = new EnumDemo();
    
        //但我们可以通过类直接访问类的对象
        EnumDemo sunday = EnumDemo.SUNDAY;
        sunday.setName("sunday");
        System.out.println("通过get/set方法进行传参/读参:" + sunday.getName());
    
        /*枚举的扩展API*/
        //通过values()拿到枚举类的全部对象
        EnumDemo[] enumArr = EnumDemo.values();
        System.out.println("enumArr[]的内容:" + Arrays.toString(enumArr));
    
        //通过valueOf()由在枚举类中查找并返回对应名称的枚举常量
        //传送的值必须与对应枚举对象相同(包括大写),否则运行时将会抛出异常
        EnumDemo monday = EnumDemo.valueOf("MONDAY");
        System.out.println("monday对应的枚举常量名:" + monday.name());
        System.out.println("monday对应的对象索引:" + monday.ordinal());
        //枚举类的对象数组也可以执行API
        System.out.println("对象数组:" + enumArr[0].name());
    }
    }

    8.2 应用场景

9.泛型

To be continued