类和对象

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

定义类

一个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. 推荐:无论是否使用,把无参构造和带全部参数的构造都写上。

对象内存图

86ce0a77e3a242f493d740b3a7bc615e.png

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

Student s = new Student()

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

一个对象的内存图

be6f6e0058b14d0eaced342c6160c8ac.png

两个对象的内存图

e0c5fb89dfd84977a33ba998567a74f7.png

两个引用指向同一个对象

75ccadc7eac740b0a2dfec05731a4136.png

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

  • 基本数据类型:数据值存在自己的空间,赋值给其他变量,也是赋的真实的值
    fe985ab6ad1b49668ecde4ac68f34ce7.png
  • 引用数据类型:数据值存储在其他空间,自己的空间存储的是其他空间的地址值。
    7d44413b38b94fe98df341556698d3c7.png

this的内存原理

this:区分局部变量和成员变量。
本质:所在方法调用者的地址值。
185030e49e7d43409502a8c48db5b469.png

static

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

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

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

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

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

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

工具类

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

继承

继承的特点

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

子类能继承父类的东西

构造方法

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

构造方法的访问特点

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

成员变量

私有和非私有的都可以直接继承,但是私有的成员变量不能被直接使用。
1c0a9b60a73f4b40a655257f72e188b6.png

成员方法

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

虚方法表

非private、非static、非final
25c8751bd3024b3ca43d05051e22735c.png

作用:提高程序的性能、

方法重写

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

this、super使用总结

this:一个变量,表示当前方法调用者的地址(并不是每个对象里都有this)
super:表示父类存储空间
c6dab1985c4b44fbacb3ac92514492b7.png

注意:如果使用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这个类中找。
成员变量:在子类对象中,会把父类中的成员变量也继承下来。
成员方法:如果子类重写方法,在虚方法表中会把父类中的方法进行覆盖。
4d93bc3624974d43951035b7b828d5c5.png

多态的优势

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

多态的弊端

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

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

使用其他类的规则

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

final

可以修饰方法、类、变量

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

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

权限修饰符

控制一个成员可以被访问的范围,可以修饰方法、变量、构造方法、内部类
private < 缺省 < protected < public
1403ed11ca8f49c0907b34d62a592c96.png

代码块

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

}
public Student(String name) {
this.name = name;
}
}

常用:
1873509cc7b04927868a9fb188b42fd6.png
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() {
//...
}
}

内部类

在一个类的里面,再定义一个类。
5be5b756eaa847b095f13d98145cab10.png

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

// 外部类
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();
}
}

静态内部类

只能访问外部类中静态变量和静态方法,如果访问非静态的需要创建对象。
15c3fe4b606a4f1b9a023d9b7f64a144.png
创建静态内部类对象的格式:外部类名.内部类名 对象名 = 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();
}
}

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