前端初学java二(类、多态、接口、内部类、泛型)

目录

种类

Javabean类

测试类

工具类

类的初始化

构照函数

新建对象的内存图

static

继承

This

Super

虚方法表

@Override

修饰符权限

构造代码块

静态代码块

多态

前提

优点

缺点

示例

抽象方法

抽象类

接口

implements

继承

内部类

成员内部类

静态内部类

局部内部类

匿名内部类

Lambda表达式

泛型

泛型类

泛型方法

泛型接口


种类

Javabean类

不写main方法的类

测试类

含有main方法的类,在该类中可以调用javabean类。

一个java文件中可以定义多个class类,但只能有一个类是public修饰,而且public修饰的类型必须成为代码文件名(实际开发中建议还是一个文件定义一个class类)。

工具类

私有化构造方法,不让外界新建它的对象,成员方法需要定义为静态方便调用。

类的初始化

下面name成员变量未赋值默认值,初始化为null,这一块初始化会和数组默认初始化规律一样。

java 复制代码
public class Student {
    String name;
    public void study() {}
}

整数类型(byte、short、int、long):默认初始化值0。

小数类型(float、double):默认初始化值0.0。

字符类型(char):默认初始化值'\u0000'空格。

布尔类型(boolean):默认初始化值false。

引用类型(类、接口、数组、String):默认初始化值null。

构照函数

构造函数和js不同(js中固定名为constructor),它使用方法名和类名完全相同,没有返回值类型也没有void,不能带有return。

如果没有定义构造方法,系统将给出一个默认的无参数构造方法。已有构造方法则不会给出默认的构造方法。构造方法也支持重构。

新建对象的内存图

java 复制代码
Student s = new Student();
s.study()

1.加载class文件(在方法区加载,只在该类被第一次new的时候加载,后面再new会直接读取方法区中的类)

2.申明局部变量 (栈内存)

3.在堆内存中开辟一个空间

4.默认初始化成员变量(属性名中未赋值的进行默认值的初始化),

5.显示初始化成员变量(属性名中有赋值的进行赋值初始化)

6.构造方法初始化

7.将堆内存中的地址值赋值给左边的局部变量(栈内存)

8.调用类的方法,调用的是指向方法区中Student类的方法。

static

可以修饰成员方法和成员变量。

被static修饰的成员变量叫静态变量,被该类所有对象共享,可以通过对象名和类名调用(js中只能通过类名调用)。

静态变量存储在静态区(堆内存中)是随类的加载而加载。因为可以通过类名调用,所以是优先于new 对象的出现。

静态方法中无this。

继承

Java只支持单继承(只有一个父类),但可以多层继承(父类也可以继续向上继承)。

Java中所有的类都直接或间接继承于Object(和js类似都继承与Object.prototype)。

子类只能访问父类中的非私有成员。

This

表示当前方法调用者的地址值,this()表示运行本类的构造方法

Super

父类存储空间,super()表示运行父类的构造方法

子类构造方法中未显式调用父类构造方法会默认调用父类中的无参构造方法(相当于第一行中插入了super()),再执行自己。

子类构造方法中未显式调用父类构造方法必须在第一行。

super不能连续调用( super.super错的)。

虚方法表

每个类都有自己的虚方法表,它为类中非private、非static、非final的方法,

每次继承时子类会将父类的虚方法表中的方法加入到自己的虚方法表中(Object类中有5个方法在自己的虚方法表中,由于继承关系所有的类虚方法表长度大于等于5)。

面向对象进阶-06-子类到底能继承父类中的哪些内容_哔哩哔哩_bilibili

当出现重名的成员变量

java 复制代码
System.out.println(name)//从局部位置开始往上找
System.out.println(this.name)//从本类成员位置开始往上找
System.out.println(super.name)//从父类成员位置开始往上找

@Override

重写的注解(本质是覆盖了虚方法表中的方法)

子类重写父类方法时:

  1. 返回值类型子类必须小于等于父类
  2. 访问权限子类必须大于等于父类(public>protected>空)

修饰符权限

构造代码块

类中写在成员属性下的只用大括号括起来的语句(相当于没有方法名、修饰符、返回值),这部分代码块会在构造方法执行前默认先执行。

静态代码块

在构造代码块前加上static:static{//代码语句}。

这部分代码只在第一次new该类时执行,用于数据初始化。

多态

父类类型 对象名称 = 子类对象;

同一种类型的对象,在不同的情况下表现出不同的行为。它是面向对象编程中的一个核心概念,主要通过方法的重载和覆盖实现。

前提

1.有继承/实现关系

2.有父类引用指向子类对象

3.有方法重写

优点

方法中使用父类型作为参数,可以接受所有子类对象。

缺点

不能使用子类中特有的方法。

示例

java 复制代码
public class Main {
    public static void main(String[] args) {
        Ye p = new Zi();
        System.out.println(p.age);//0
        p.a();//222
    }
}

class Ye{
    public int age=0;
    public void a(){
        System.out.println("000");
    }
}

class Fu extends Ye{
    public int age=1;
    public void a(){
        System.out.println("1111");
    }
}

class Zi extends Fu{
    public int age=2;
    public void a(){
        System.out.println("222");
    }
}

成员变量 编译看左运行看左

编译看左:p.age编译时会查看对p类型定义时的Ye中是否有可继承的age属性。

运行看左:p.age运行是会查看Ye中的age属性。

成员方法 编译看左运行看右

编译看左:p.a()编译时会查看Ye中是否有a方法,没有会报错,可以通过强制类型转换避免标错((Zi)p).a()。

运行看右:p.a()方法运行时,实际访问的是构造时Zi中的函数。

抽象方法

abstract修饰的方法,无方法体。

抽象类

abstract修饰的类,一般都会带有抽象方法。

抽象类不能实例化

抽象类的子类要么重写抽象类中的所有抽象方法,要么是抽象类。

抽象方法所在的类必须是抽象类

接口

Public Interface 接口名{}

接口的子类要么重写接口中的所有抽象方法,要么是抽象类。

接口中成员变量默认使用public static final

成员方法默认使用public abstract修饰。

implements

接口和类之间是实现关系,通过implements关键字表示。

1个类可以实现多个接口public class 类名 implements 接口名1,接口名2(接口可以有重名的,只用重写一次)

继承

接口和接口之间可以单继承也可以多继承,但是在实现类时需要实现该接口以及继承的所有父接口中定义的抽象方法。

JDK8以后接口中可以用default修饰有方法体中的方法,该方法在实现的类中不需要被重写(如果重写的话类中的该方法不能用default修饰)。

如果一个类实现了多个接口,多个接口中存在相同名字的默认方法,那么该类就必须对方法进行重写。

JDK8以后接口中可以用static修饰静态方法,静态方法使用接口名.静态方法名调用。不能重写。

JDK9新增了private修饰接口中的成员方法,如果修饰的为静态方法,表示只能在该接口中的静态方法中调用,如果修饰的为defautl方法,表示可以在default修饰方法中调用。在该接口外都无法调用

内部类

可以直接访问外部类的成员,包括私有的。

外部类访问内部类的成员必须创建内部类的对象。

成员内部类

java 复制代码
public class Car {
    String carName;
    class Engine {
        String engineName;
    }
}

可以被private、空、protected、public、static修饰符所修饰。外部访问权限和修饰类方法相同。

JDK16之前成员内部类里不能定义静态变量。

外部创建内部类方式

外部类名.内部类名 对象名 = 外部类对象.内部类对象

java 复制代码
public class Main {
    public static void main(String[] args) {
        Ye.Fu obj = new Ye().new Fu();
        System.out.println(obj.age);//1
        obj.a();//1111
    }
}
class Ye{
    public int age=0;
    class Fu {
        public int age=1;
        public void a(){
            System.out.println("1111");
        }
    }
}

在内部类的方法可以通过外部类名.this.成员变量名访问外部类中的成员变量。

java 复制代码
public class Main {
    public static void main(String[] args) {
        Ye.Fu obj = new Ye().new Fu();
        obj.show();
    }
}
class Ye{
    public int age=0;
    class Fu {
        public int age=1;
        public void show(){
            int age = 2;
            System.out.println(Ye.this.age);//0
            System.out.println(this.age);//1
            System.out.println(age);//2
        }
    }
}

静态内部类

内部类中以static修饰,只能访问外部类中的静态变量和静态方法,如果想要访问非静态的需要创建对象。

创建内部内对象格式:外部类名.内部类名 对象名 = new 外部类名.内部类名()

局部内部类

将内部类定义在方法里面,类似于方法里面的局部变量。可以用final修饰不能用private等权限修饰符修饰。

外界无法直接使用局部内部类,需要在方法内部创建对象使用。

该类可以直接访问外部类的成员,也可以访问方法内的局部变量。

匿名内部类

New 类名或者接口名(){

重写方法

}

java 复制代码
public class Main {
    public static void main(String[] args) {
        new Swim(){
            @Override
            public void swim() {
                System.out.println("重写了swim方法");
            }
        };
    }
}
interface Swim {
    public abstract void swim();
}

Lambda表达式

JDK8+

用于简化匿名内部类的书写方式,只能简化函数式接口的匿名内部类的写法。

函数式接口:有且仅有一个抽象方法的接口,接口上方可以加@FunctionalInterface注解。

Lambda的省略规则

  1. 参数类型可以省略不写,
  2. 如果只有一个参数,参数类型可以省略,同时()也可以省略
  3. 如果lambda表达式的方法体只有一行,大括号,分号,return可以省略不写,需要同时省略。
java 复制代码
public class Main {
    public static void main(String[] args) {
        Integer[] arr = {2,3,6,1,4,7,8};
        Arrays.sort(arr, new Comparator<Integer>() {
            @Override
            public int compare(Integer a, Integer b) {
                return a-b;
            }
        });
        Arrays.sort(arr,(Integer a,Integer b)->{
            return a-b;
        });
        System.out.println(Arrays.toString(arr));//[1, 2, 3, 4, 6, 7, 8]

    }

}

泛型

可以在编译阶段约束数据类型,只能支持引用数据类型。

不写泛型默认是Object。

运行时是没有泛型的,编译后会进行运行擦除。

泛型类

java 复制代码
public class Main {
    public static void main(String[] args) {
        MyArrayList<String> list = new MyArrayList<>();
    }
}
class MyArrayList<T>{}

上面的T对应到new 对象时候的String

泛型方法

java 复制代码
public class Main {
    public static void main(String[] args) {
        MyArrayList.addAll(new ArrayList<Integer>(),1,2);
    }
}
class MyArrayList{
    public static <T> void addAll(ArrayList<T> list,T a,T b){
       list.add(a);
       list.add(b);
       System.out.println(list.toString());//[1, 2]
    }
}

泛型不具备继承性,但是数据具备继承性

指定泛型具体类型后,传递数据时,可以传入该类型和他的子类型。但调用方法参数不能是不同泛型变量。

java 复制代码
class Ye{}
class Fu extends Ye{}
class Zi extends Fu{}
public class Main {
    public static void main(String args[]) {
        ArrayList<Ye> list1 = new ArrayList();
        ArrayList<Fu> list2 = new ArrayList();
        ArrayList<Zi> list3 = new ArrayList();
        method(list1);
        method(list2);//报错
        method(list3);//报错
        list1.add(new Ye());
        list1.add(new Zi());
        list1.add(new Fu());
    }
    public static void method(ArrayList<Ye> list){}
}

泛型接口

修饰符 interface 接口名<类型>{}

泛型通配符?

?extends T:表示可以传递T或者T的所有子类型

?super T:表示可以传递T或者T的所有父类型。