javase-类和对象

42 阅读12分钟

类和对象

类:对象共同特征的描述 对象:真实存在的东西

定义类

一个java文件中可以定义多个类,但是只能一个类是public修饰,public修饰的类名必须是Java代码的文件名。

public class 类名 {
	1.成员变量
	2.成员方法
	3.构造器
	4.代码块
	5.内部类
}

JavaBean类

在JavaBea类中不写main方法,用来描述一类事物 标准的JavaBean

  • 类名见名知意
  • 成员变量使用private修饰
  • 至少提供两个构造方法(无参、带全部参数)
  • 提供每一个成员变量对应的get和set方法

测试类

创建javaBean类的对象并复制调用,带main方法

封装

对象代表什么,就得封装对应的数据,并提供数据对应的行为。 例::画圆这个方法应该是定义在圆类里边。【人调用了圆类画圆的方法】

构造方法

在创建对象的时候给成员变量进行初始化的。 执行时机:创建对象的时候由虚拟机调用。

public class Stu {
	修饰符 类名(参数) {
		方法体;
	}
}
  1. 方法名和类名相同
  2. 没有返回值(不能有return待会结果数据)
  3. 如果没有写构造方法,虚拟机会默认给一个空参的构造方法。
  4. 如果已经定义了构造方法,虚拟机就不会给空参的构造方法。
  5. **推荐:**无论是否使用,把无参构造和带全部参数的构造都写上。

对象内存图

在这里插入图片描述

虚拟机创建一个对象的过程

Student s = new Student()

  1. 加载class文件
  2. 申明局部变量
  3. 在堆内存中开辟空间
  4. 默认初始化
  5. 显式初始化
  6. 构造方法初始化
  7. 将堆内存中的地址值赋值给左边的局部变量

一个对象的内存图

在这里插入图片描述

两个对象的内存图

在这里插入图片描述

两个引用指向同一个对象

在这里插入图片描述

基本数据类型和引用数据类型

  • 基本数据类型:数据值存在自己的空间,赋值给其他变量,也是赋的真实的值 在这里插入图片描述
  • 引用数据类型:数据值存储在其他空间,自己的空间存储的是其他空间的地址值。 在这里插入图片描述

this的内存原理

this:区分局部变量和成员变量。 本质:所在方法调用者的地址值。 在这里插入图片描述

static

修饰成员变量(静态变量)

特点:该类被所有对象共享 调用方式:类名调用、对象名调用

静态变量是随着类的加载而加载,属于类,不属于对象。

修饰成员方法(静态方法)

特点:多用于测试类和工具类中,javaBean中很少用 调用方式:类名调用、对象调用

静态方法只能访问静态变量和静态方法 非静态方法可以访问所有的变量和方法(静态、非静态) 静态方法中没有this关键字【非静态方法虚拟机会分配一个this对象,表示当前方法调用者的地址,这个this不需要我们来赋值】

工具类

  • 需要私有化构造方法(工具类的对象没有实际意义,可以通过类名直接调用工具类里的方法)
  • 方法定义为静态的

继承

继承的特点

  • java只支持单继承,不支持多继承,但支持多层继承
  • 所有的类都直接或间接继承于Object类

子类能继承父类的东西

构造方法

父类的构造方法不能被子类继承

构造方法的访问特点
  1. 父类中构造方法不会被子类继承
  2. 子类中所有的构造方法都先默认访问父类中的无参构造,再执行自己。
  3. 子类构造方法的第一行默认都是:super(),不写虚拟机也会自动加上。

成员变量

私有和非私有的都可以直接继承,但是私有的成员变量不能被直接使用。 在这里插入图片描述

成员方法

只有父类中的虚方法才能被子类继承

虚方法表

非private、非static、非final 在这里插入图片描述

作用:提高程序的性能、

方法重写
  1. 重写方法、形参列表和父类一致
  2. 方法的访问权限子类访问权限必须大于等于父类
  3. 方法的返回值类型子类必须小于等于父类
  4. 只有被添加到虚方法表中的方法才能被重写

this、super使用总结

this:一个变量,表示当前方法调用者的地址(并不是每个对象里都有this) super:表示父类存储空间 在这里插入图片描述

注意:如果使用this(...)访问本类其他构造,虚拟机就不会再添加super()了,因为this(...)访问的构造方法里就会自动加上super()

多态

多态:对象的多种表现形态

  • 变量调用:编译看左边,运行看左边。
  • 方法调用:编译看左边,运行看右边。
class Animal {
    String name = "动物";
    public void show() {
        System.out.println("animal show()...");
    }
}
class Dog extends Animal {
    String name = "狗";
    public void show() {
        System.out.println("dog show()...");
    }
}
public class Demo01 {
    public static void main(String[] args) {
        Animal a = new Dog();
        /**
         * 调用成员变量:编译看左边,运行看左边
         * 编译看左边:javac编译时,会看左边的父类中有没有这个变量,如果有,编译成功;没有,编译报错。
         * 运行看左边:java运行时,实际获取的就是左边父类中成员变量的值。
         */
        System.out.println(a.name); // 动物
        /**
         * 调用成员方法:编译看左边,运行看右边
         * 编译看左边:javac编译时,会看左边的父类中有没有这个变量,如果有,编译成功;没有,编译报错。
         * 运行看右边:java运行代码时,实际上运行的事子类中的方法
         */
        a.show();// dog show()...
    }
}

理解: Animal a = new Dog(); a是Animal类的,所以默认都会从Animal这个类中找。 成员变量:在子类对象中,会把父类中的成员变量也继承下来。 成员方法:如果子类重写方法,在虚方法表中会把父类中的方法进行覆盖。 在这里插入图片描述

多态的优势

  1. 右边可以实现解耦合
  2. 定义方法时,使用父类作为参数,可以接受所有的子类对象。

多态的弊端

不能调用子类特有的方法【通过强转成子类来调用子类特有的方法】

包就是文件夹,管理不同功能的Java类。 类的全类名 = 包名 + 类名

使用其他类的规则

  1. 使用同一个包中的类,不需要导包
  2. 使用java.lang包中的类,不需要导包
  3. 其他情况都需要导包
  4. 如果使用两个包中同名的类,需要导包

final

可以修饰方法、类、变量

  1. 修饰方法:表明当前方法是最终方法,不能被重写【当前的方法是一种规则, 不希望别人去改变】
  2. 修饰类:表明当前方法是最终类,不能被继承
  3. 修饰变量:被修饰的变量叫做常量,只能被赋值一次

final修饰的变量是基本类型:变量存储的数据值不能发生改变。 final修饰的变量是引用类型:变量存储的地址值不能发生改变,对象的内部可以改变。

权限修饰符

控制一个成员可以被访问的范围,可以修饰方法、变量、构造方法、内部类 private < 缺省 < protected < public 在这里插入图片描述

代码块

  1. 局部代码块(淘汰)
  2. 构造代码块:写在成员位置的代码,优先于构造方法执行,可以把多个构造方法中重复的代码写在构造代码块中(淘汰)
public class Student {
	private String name;
	{// 构造代码块,先于构造方法执行
		System.out.print("开始创建对象了");
	}
	public Student() {
		
	}
	public Student(String name) {
		this.name = name;
	}
}

常用: 在这里插入图片描述 3. 静态代码块 需要通过static关键字修饰,随着类的加载而加载,自动触发,只执行一次。 格式:static{} 使用场景:在类加载的时候,做一些数据初始化的时候使用

class Stu {
    private String name;
    private int age;
    static {
        System.out.println("静态代码块执行了");
    }
}
public class Demo03 {
    public static void main(String[] args) {
        Stu stu1 = new Stu();
        Stu stu2 = new Stu();
    }
}

抽象类

将共性的方法抽象到父类之后,在父类中不确定具体的方法体,该方法就可以定义为抽象方法

  • 抽象类不能创建对象
  • 抽象类可以有构造方法
  • 抽象类不一定有抽象方法,有抽象方法的类一定是抽象类
  • 抽象类的子类要么重写抽象类中所有的抽象方法,要么子类也得是抽象类。

接口

接口就是一种规则。 抽象类表示一类事务,接口表示一种行为。

public interface 接口名{}
  • 接口不能实例化
  • 接口和类之间是实现关系
public class 类名 implements 接口名{}
  • 接口的子类:要么重写接口中所有的抽象方法,要么是抽象类。

注意:

  1. 接口和类的实现关系可以是单实现,也可以是多实现。
  2. 实现类可以在继承一个类的同时实现多个接口。 public class 类名 extends 父类 implements 接口名1, 接口名2 {}

接口中成员的特点

  1. 成员变量:只能是常量,默认修饰符:public static final
  2. 构造方法:没有
  3. 成员方法:抽象方法 或 有方法体的方法,默认修饰符:public abstract

接口和类之间的关系

  1. 类和类的关系 继承关系,只能单继承,不能多继承,可以多层继承。
  2. 类和接口的关系 实现关系,可以单实现,也可以多实现,还可以再继承一个类的同时实现多个接口。
  3. 接口和接口的关系 继承关系,可以单继承,也可以多继承。

JDK8接口新增的方法

默认方法

JDK8以后:接口可以定义有方法体的方法,允许在接口中定义默认方法,需要用关键字default修饰 格式:public default 返回值类型 方法名(参数列表) {}

  1. 默认方法不是抽象方法,不强制重写,如果被重写,重写的时候需要去掉default关键字。
  2. public可以省略,default不能省略。
  3. 如果实现了多个接口,多个接口存在相同名字的默认方法,子类就必须对该方法进行重写。

静态方法

允许在接口中定义静态方法,需要用static修饰。 格式:public static 返回值类型 方法名(参数列表) {}

  1. 静态方法只能通过接口名调用,不能通过实现类 或 对象名调用。
  2. public可以省略,static不能省略。

接口的应用

  1. 接口代表规则,是行为的抽象,想要让哪个类拥有一个行为,就让这个类实现对应的接口。
  2. 接口多态:当一个方法的参数是接口时,可以传递接口所有实现类的对象

适配器模式:解决接口与接口实现类之间的矛盾问题

场景:假设一个接口有多个抽象方法,想让某个继承这个接口的类只实现其中一个方法。

interface Inter {
    public abstract void fun1();
    public abstract void fun2();
    public abstract void fun3();
}
// 中间写一个适配器,空实现Inter接口的所有方法。
abstract class InterAdapter implements Inter { // 使用abstract是为了防止InterAdapter创建对象,因为没有意义

    @Override
    public void fun1() {

    }

    @Override
    public void fun2() {

    }

    @Override
    public void fun3() {

    }
}
// 如果只想要实现Inter接口的第三个抽象方法(如果还想要继承其他类,也可以让中间的适配器类去继承其他的类)
public class InterImpl extends InterAdapter {
    // 需要用到哪个方法,就重写哪个方法
    @Override
    public void fun3() {
        //...
    }
}

内部类

在一个类的里面,再定义一个类。 在这里插入图片描述

内部类表示的事物是外部类的一部分,单独出现没有任何意义。 内部类可以访问外部类的成员,包括私有。 外部类要访问内部类的成员,必须创建对象。

// 外部类
public class Car {
    private String carName;
    int carAge;
    String carColor;
    public void show() { // 外部类要访问内部类的成员,必须创建对象
        System.out.println(carName);
        // System.out.println(engineName); // 代码报错
    }
    // 内部类
    class Engine {
        String engineName;
        int engineAge;
        public void show() { // 内部类可以直接访问外部类的成员,包括私有。
            System.out.println(engineName + carName);
        }
    }
}

成员内部类

写在成员位置,属于外部类的成员。

class Outer {
    private String a = "Outer";
    class Inner {
        String a = "Inner";
        public void show() {
            System.out.println(a); // Inner
            System.out.println(this.a); // Inner
            System.out.println(Outer.this.a); // Outer【Outer.this:获取外部类对象的地址值】
        }
    }
    public Inner getInstance() {
        return new Inner();
    }
}
public class Demo04 {
    public static void main(String[] args) {
        // 获取成员内部类的对象
        // 方法1:直接创建(内部类被非私有修饰)
        Outer.Inner oi1 = new Outer().new Inner();
        // 方法2:通过get方法(内部类使用private修饰)
        Outer o = new Outer();
        Outer.Inner oi2 = o.getInstance();
    }
}

静态内部类

只能访问外部类中静态变量和静态方法,如果访问非静态的需要创建对象。 在这里插入图片描述 创建静态内部类对象的格式:外部类名.内部类名 对象名 = new 外部类名.内部类名() 调用非静态方法的格式:先创建对象,用对象调用 调用静态方法的格式:外部类名.内部类名.方法名()

局部内部类

  1. 将内部类定义在方法里就叫局部内部类,类似于方法里的局部变量。
  2. 外界无法直接使用,需要在方法内部创建对象并使用。
  3. 可以直接访问外部类的成员,也可以访问方法里的局部变量。

匿名内部类(重点)

匿名内部类的本质就是隐藏了名字的内部类。

new 类名或接口名() {
	重写方法;
}
interface Swim {
    public abstract void swim();
}
abstract class Animal {
    public abstract void eat();
}
public class Demo05 {
    public static void main(String[] args) {
        Swim swim = new Swim() {
            @Override
            public void swim() {
                System.out.println("swim...");
            }
        };

        // 如果这个类只使用一次,可以考虑定义成匿名内部类
        method(new Animal() {
            @Override
            public void eat() {
                System.out.println("eat...");
            }
        });
    }

    public static void method(Animal a) {
        a.eat();
    }
}

使用场景,如果定义的类或接口只使用一次,可以使用匿名内部类简化代码。