OOP 面向对象 java 基础

0 阅读13分钟

java运行机制

JDK:Java Development Kit (开发者工具包)

JDK ⊃ JRE ⊃ JVM

image.png

内存和内存地址

内存:软件在运行过程中,临时存储的数据

内存地址:内存会以字节(1byte)为单元,划分成很多区域,每个区域对应一个编号

地址范围:(会转化为16进制,方便阅读)

image.png

image.png

基本数据类型:栈内存直接存值

应用数据类型:栈内存中存储值的索引

image.png

数组

1. 存储为连续的空间

2. 一旦定义,长度不变

静态初始化数组

image.png

遍历数组

int[] ageArr1 = new int[]{1, 2, 3, 4, 5, 6};

for (int i = 0; i < ageArr1.length; i++) {
    System.out.println(ageArr1[i]);
}

for (int j : ageArr1) {
    System.out.println(j);
}

动态初始化

image.png

默认初始化的值

image.png

方法

// 代码结构
public static 返回值类型 方法明(参数1, 参数2) {
    方法逻辑
    return 返回值;
}

// 示例
public static int sum(int a, int b) {
    int result = a + b;
    return result;
}

方法的重载

1. 方法名相同
2. 但各自的(形参)参数不同【个数不同,类型不同,顺序不同,都算】
称为方法重载(`Overload`)。
public static void myHello() {
    System.out.println("你好");
}

public static void myHello(String name) {
    System.out.println("你好,我是" + name);
}

public static void myHello(String name, int age) {
    System.out.println("你好,我是" + name + "我今年" + age);
}

不构成方法的重载,重载只看方法名和参数

public static void myHello() {
    System.out.println("你好");
}

public static String myHello() {
    return "你好";
}

面向对象(一种编程思想)

class和instance class类,就相当于模具, instance实例,就相当于模具中生产的东西

image.png

测试类和javabean类

image.png

javabean类 (描述一类事物)

package classStuden;

public class People {
    // 属性
    String name;
    int age;
    // 行为,方法
    public void say() {
        System.out.println("我是人类");
    }
}

测试类(带有main方法,是程序的主入口)

package classStuden;

public class Test {
    public static void main(String[] args) {
        People p1 = new People();
        p1.say();
    }
}

工具类 (帮我们做事情的类,如求最大值啊之类的)

public class ArrayUtil {
    // 私有化构造方法,目的:不让外界创建构造函数
    private ArrayUtil() {
    }

    //获取数组的平均值
    public static void getAverage(int[] arr) {
        int sum = 0;
        int length = arr.length;
        for (int j : arr) {
            sum += j;
        }
        System.out.println("该数组的平均值是:" + sum * 1.0 / length);
    }
}

访问控制修饰符【权限修饰符】(4 个)

public String name;
protected String sexy;
int age; // 没有修饰符,默认就是default
private int score;

image.png

image.png

不控制访问权限,而是定义特性、状态、行为,比如静态、不可变、抽象等。

image.png

static 关键字

不属于对象,属于类

在class里,定义共享数据,无论哪个实例化对象修改了这个值,所有实例化对象的值都会被修改**(所有对象共享)**

静态变量是随着类加载而加载的,优先于对象出现的(也就是说,类还没有实例化之前,静态变量就在堆内存中开辟空间了)

public class Student {
    static String teacher;
    String name; // 成员变量
    int age;

    // 构造方法
    public Student(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public void getTeacherName() {
        System.out.println("老师的名字是:" + Student.teacher);
    }
}

public class Test {
    public static void main(String[] args) {
        Student s1 = new Student("小明", 18);
        Student s2 = new Student("小红", 20);

        Student.teacher = "张雪峰";

        s1.getTeacherName(); // 输出:张雪峰
        s2.getTeacherName(); // 输出:张雪峰
    }
}

image.png

static 修饰静态方法

静态方法多用在测试类和工具类中,javabean类很少使用

  1. 静态方法只能访问静态变量和其他静态方法
  2. 非静态方法,可以访问其他静态变量和其他静态方法,也可以访问非静态的变量和方法
  3. 静态方法里面不能用this关键字,要用 类名.静态变量(静态方法)

image.png

public class StaticUtil {
    String name; // 大名
    static String xiaoName; // 小名

    int age; // 岁数
    int virtualAge; // 虚岁

    public void getName() {
      // 可以访问所有变量和方法
    }

    public static void getXiaoName() {
    //    只能访问静态变量(xiaoName, virtualAge)和静态方法(getVirtualAge)
    }

    public void getAge() {
    }

    public static void getVirtualAge() {
    }
}

public和static的区别是什么

public 是权限修饰符,控制在别的包里面,能不能访问你

static 是指将方法或者变量归结到类上,随着类的加载而加载,可以直接使用 类名.方法名 的形式调用,不需要new

public 就像 js 里面的 export

public static 组合使用就约等于 export function

final 关键字(不可变 / 不可修改 / 不能被覆盖)相当于JS里面的 const

image.png

this关键字

用于区分成员变量和局部变量的

public class Student {
    String name; // 成员变量

    public void setName(String name) { // 局部变量(参数)
        // 这里 this.name = 成员变量
        // 这里 name = 传入的参数
        this.name = name;
    }
}

本质:this指的就是所在方法调用者的地址值,谁调用就指向谁

public class Student {
    String name; // 成员变量
    int age;

    // 构造方法
    public Student(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public void getName() {
        String name = "局部名称";
        // 就近原则
        System.out.println("局部变量的名称" + name);
        // 获取成员变量
        System.out.println("成员变量的名称" + this.name);
    }
}
public class Test {
    public static void main(String[] args) {
        Student s1 = new Student("小明", 18);
        Student s2 = new Student("小红", 20);

        s1.getName(); // 此时this指向s1这个地址
        s2.getName(); // 指向s2的地址
    }
}

构造方法,构造函数

作用:在创建(实例化)对象的时候,给成员变量赋值

构造方法的特点:

  • 必须和类名一样,且不写的时候,会默认帮你创建一个无参数的构造方法
  • 如果写了任意构造方法,系统不再提供无参数的构造方法
  • 构造方法也可以重载
  • 没有返回值类型,也没有void
  • 没有具体返回值,也不能写return

构造方法的执行时机:

  • 创建对象时,由虚拟机调用,不能手动调用
  • 每创建一次对象,就会调用一次构造方法
public class People {
    // 属性
    public String name;
    protected String sexy;
    int age;
    private int score;
    
    public People() {
    }

    public People(String name, String sexy, int age, int score) {
        this.name = name;
        this.sexy = sexy;
        this.age = age;
        this.score = score;
    }
}

People p1 = new People("cmh","男", 18, 100);

枚举(枚举类,特殊的类)

可创建的对象个数是有限的

默认继承 Enum

每一个枚举项(SPRING、SUMMER)都是 该类的 public static final 实例对象

枚举类第一行必须是枚举项,以逗号分割,分号结束

枚举类的构造方法,必须是private,目的是不然外界创建超过数量的对象

public enum 枚举类名 {

    // 属性

    // 方法
    
}
public enum SeasonEnum {
    // 这里其实都是相当于使用 new 实例化出来的对象,调用的就是下面的 SeasonEnum 构造方法,
    // 所以在使用的时候,直接使用【类名.枚举值】的形式就可以了,不需要再new了
    // 且这里的枚举值,默认都是使用 【 public static final 】 修饰的
    SPRING(1, "春天"),
    SUMMER(2, "夏天"),
    AUTUMN(3, "秋天"),
    WINTER(4, "冬天");


    // 枚举可以有成员变量
    private final int code;
    private final String name;

    // 构造方法(默认就是 private,且必须是private,不能 public)
     SeasonEnum(int code, String name) {
        this.code = code;
        this.name = name;
        System.out.println("构造函数被执行了:" + this.name); // 会发现直接输出四个
    }

    // 可以写普通方法
    public String getInfo() {
        return code + ":" + name;
    }
}

面向对象的三大特征(封装、继承、多态)

继承 extends

什么是继承

父类是通用模板,子类是在模板基础上定制化的版本。

继承的目的

  1. 代码复用
  2. 扩展功能
  3. 多态的基础,没有继承就没有多态

继承的重要规则

  1. Java 只支持单继承,一个子类只能继承一个父类,但是可以多层继承,就像原型链一样,继承的顶点是object
  2. 子类不能继承父类的私有成员,private 修饰的属性和方法,子类无法直接访问
  3. 构造方法不能被继承,只能通过 super() 调用
  4. 子类可以重写(override)父类方法

image.png

什么是重写

子类把父类的方法重新写一遍,方法名、参数列表、返回值都相同,只有方法体不同。

在重写的方法体中,可以使用 super.方法 的形式,调用父类的方法,正在被重写的这个方法也能被调用

super 关键字(继承必备)

super 代表父类对象,用于子类中访问父类的成员。

  1. 调用父类构造方法super();
  2. 调用父类属性super.属性名
  3. 调用父类被重写的方法super.方法名()
public class Student extends  People {
    
    // 可以把多个子类中的方法,提取到父类中去,提高代码的复用性
    // 子类可以在父类的基础上,增加其他功能,使子类更强大

}

多态(事物的多种形态)

什么是多态

同一个行为,在不同对象上表现出不同的形态 / 结果

父类引用指向子类对象,调用重写的方法时,会执行子类的实现,而不是父类的。

一句话总结:编译看左边,运行看右边

多态的前提条件(必须同时满足)

  1. 继承 / 实现:必须有子类继承父类,或实现类实现接口
  2. 方法重写:子类必须重写父类的方法(多态的核心)
  3. 父类引用指向子类对象父类 引用名 = new 子类();
public class Animal {
    // 父类方法
    public void shout() {
        System.out.println("动物叫");
    }
}
public class Dog extends Animal {
    // 重写父类方法
    @Override
    public void shout() {
        System.out.println("汪汪汪");
    }
}
public class Cat extends Animal {
    // 重写父类方法
    @Override
    public void shout() {
        System.out.println("喵喵喵");
    }
}
public class Test {
    public static void main(String[] args) {
        // 多态:父类引用 指向 子类对象
        Animal animal1 = new Dog();
        Animal animal2 = new Cat();

        // 同一个方法 shout(),执行结果不同!
        animal1.shout(); // 输出:汪汪汪
        animal2.shout(); // 输出:喵喵喵
    }
}

多态现象

animal1animal2都是Animal类型,但调用同一个shout()方法,执行的是各自子类的代码。这就是多态

多态的作用(为什么要用它?)

  1. 提高代码的可扩展性 (最核心作用)
  2. 减少重复代码,简化程序
  3. 解耦:降低代码耦合度
  4. 统一程序设计标准

多态中成员变量 / 静态方法的特点(面试常考)

成员变量:没有多态!永远看左边(父类)

静态方法:没有多态!永远看左边(父类)

只有【实例方法】存在多态

class Animal {
    String name = "动物";
    public static void sleep(){
        System.out.println("动物睡");
    }
}

class Dog extends Animal {
    String name = "小狗";
    public static void sleep(){
        System.out.println("小狗睡");
    }
}

// 测试
Animal a = new Dog();
System.out.println(a.name); // 动物(变量无多态)
a.sleep(); // 动物睡(【静态方法】无多态)

多态的向上转型与向下转型

  1. 向上转型(自动发生)

父类 引用 = new 子类();

就是我们上面写的多态,安全、自动

  1. 向下转型(强制转换)

目的:把 “伪装成父类” 的子类对象,变回子类本身,从而调用子类独有的方法和属性

必须配合instanceof判断,否则会报类型转换异常。

抽象类(abstract)

抽象类就是:被设计成 “专门用来被继承、不能直接 new” 的半完成类。

它的作用就是强制子类必须重写抽象方法,不带abstract关键字的方法可以不用重写。

1. 关键字:abstract

  • 抽象类:abstract class
  • 抽象方法:abstract void 方法名(); 没有方法体
// 抽象类
public abstract class User {
    String name;

    // 抽象方法:只有声明,没有实现
    public abstract void work(参数);
}

抽象类不能被实例化new User(); // 报错

抽象类里可以有普通方法、属性、构造方法

有抽象方法的类,必须是抽象类(抽象类中可以没有抽象方法,但是有抽象方法的必须是抽象类)

子类继承抽象类,必须重写所有抽象方法,否则报错(强制规范,保证子类一定有某个方法。)

抽象类到底用来干嘛?(核心作用)

  1. 强制子类必须重写方法
  2. 把通用代码抽到父类,子类只写自己的逻辑
  3. 配合多态使用

接口(Interface)

接口就是一套纯规范、纯规则,只定义 “要做什么”,不写 “怎么做”。

接口只规定必须有哪些方法,具体逻辑让实现类自己写。

特点:

  1. 接口不能被实例化,也就是new
  2. 接口的子类叫实现类,一个实现类可以有多个接口,叫多实现,也可以在继承一个类的同时实现多个接口
  3. 实现类要么重写接口的所有方法,要么实现类本身就是一个抽象类
// 先 extends 继承,再 implements 实现多个接口
public class Student extends Person implements Study, Work {

    // 实现接口1的方法
    @Override
    public void study() {
        System.out.println("学生在学习");
    }

    // 实现接口2的方法
    @Override
    public void work() {
        System.out.println("学生的工作是学习");
    }
}

接口的成员特点:

  1. 成员变量只能是产量,默认 public static final 修饰
  2. 成员方法主要是抽象方法,默认被 public abstract 修饰(JDK8和JDK9之后有扩展)
  3. 接口没有构造方法
// 接口
public interface Animal {
    // 方法默认就是 public abstract
    void shout();
}

// 实现类
public class Dog implements Animal {
    @Override
    public void shout() {
        System.out.println("汪汪汪");
    }
}

接口和接口之间可以继承,也可以多继承

// 接口1
interface A {
    void testA();
}

// 接口2
interface B {
    void testB();
}

// 接口3
interface C {
    void testC();
}
// D 接口同时继承 A、B、C 三个接口
interface D extends A, B, C {
    void testD();
}

JDK8和JDK9之后的扩展

e164fea9-25c9-45ee-8bda-440b0b809e40.png

内部类

  1. 成员内部类
  2. 静态内部类
  3. 局部内部类
  4. 匿名内部类(最重要,最常用)

image.png

成员内部类

public class Outer {
    private int a = 10;

    // 成员内部类
    class Inner {
        public void show() {
            // 直接访问外部类私有变量
            System.out.println(a);
        }
    }
}

创建步骤

Outer outer = new Outer();
Outer.Inner inner = outer.new Inner();
inner.show();

静态内部类(static 修饰)

public class Outer {
    private static int b = 20;

    // 静态内部类
    static class StaticInner {
        public void show() {
            System.out.println(b);
        }
    }
}

创建步骤

Outer.StaticInner inner = new Outer.StaticInner();

局部内部类(方法里的类)

public class Outer {
    public void method() {
        int num = 100;

        // 局部内部类
        class LocalInner {
            public void show() {
                System.out.println(num);
            }
        }

        // 只能在方法内创建对象
        LocalInner inner = new LocalInner();
        inner.show();
    }
}

匿名内部类(最重要!开发大量用)

作用:在继承一个类或者实现一个接口的时候少写一个文件,用来快速创建一个 “一次性使用” 的实现类 / 子类对象,也是lambda的前置知识点

父类或接口 对象名 = new 父类或接口() {
    // 必须重写所有抽象方法
    @Override
    public void 方法名() {
        // 方法体
    }
};
  • 没有类名,所以叫 “匿名”

  • 只能创建一个对象,用完就丢

  • 可以直接访问外部类的所有成员(包括 private)

  • 可以访问方法里的变量,但变量必须是 final 或 有效 final

  • 本质就是:快速创建子类 / 实现类对象

匿名内部类示例:

先定义一个接口:

interface Study {
    void study();
}

不用匿名内部类(传统写法,有名字的实现类)

class Student implements Study {
    @Override
    public void study() {
        System.out.println("学生在学习");
    }
}

// 使用
Study s = new Student();
s.study();

用匿名内部类(一步搞定)

Study s = new Study() {
    @Override
    public void study() {
        System.out.println("学生在学习");
    }
};

s.study();

为什么这里可以使用接口实例化,其实不是实例化,

【 这里其实是在创建一个 “匿名的、实现了 Study 接口的子类对象”,并用这个匿名的实现类进行实例化的 】

为什么可以直接用Study s来接收实例化?

【 这其实是多态的体现 --> 父 父 = new 子() / User user = new Student()

new 没有名字的 java 类 + 继承/实现 + 重写方法 + 创建对象