Java复习笔记-11

面向对象

补充:类加载过程

java文件经过编译成为class文件,class文件加载到内存中。

class文件在加载到内存的过程中分为:

加载 — 将 class 文件转换为二进制的字节码

校验 — 检查字节码的安全性

准备 — 将静态变量放到方法区分配空间,然后标记一个默认值,标记的默认值可能会被舍弃

解析 — 常量符号引用的替换(暂时用不上)

初始化 — 执行静态变量的赋值以及静态代码块,这个没有特定的先后顺序,代码谁在上先执行谁。

  • 情况一
class A{
    // 先将静态变量i放入方法区,然后标记一个默认值0,检查变量i是否具有具体的初始化值.
    // 有初始化值3,舍弃标记值将3作为初始化值设置进去。然后执行静态代码块,将i的值改为5
    static int i = 3;
    static {
        i = 5;
    }
}
  • 情况二
class A{

    // 在准备阶段,先将静态变量i放入方法区,并且标记默认值为0.到了初始化阶段,先执行静态代码块。将i的标记值由0改成5
    // 然后执行i的赋值,发现有初始化值3,所以抛弃标记值5,将3作为最终的值设置进去。
    static {
        i = 5;
    }
    static int i = 3;

}
  • 情况三
class A{

    // 在准备阶段,先将静态变量i放入方法区,并且标记默认值为0.到了初始化阶段,先执行静态代码块。将i的标记值由0改成5
    // 然后执行i的赋值,发现没有初始化值,把标记值5作为最终的值设置进去。
    static {
        i = 5;
    }
    static int i;

}
  • 情况四
class A{

    // 在准备阶段,先将静态变量i放入方法区,并且标记默认值为0.到了初始化阶段,发现i没有初始化值,然后把默认的标记值0赋值给i
    // 然后执行 i += 5,结果是5 
    static int i;
    static {
        i += 5;
    }

}
  • 情况五
class A{

    // 静态变量在没有执行赋值语句之前(标记值状态),不允许直接运算
    // 这种情况下先执行静态代码块,i还是在标记值状态,所以不能运算,编译失败
    static {
        i += 5;
    }
    static int i;

}

练习:

class SDemo{
    // 准备阶段:加载sd标记值为null,i标记值是0,j标记值是0.
    // 初始化阶段: sd=new SDemo();就要执行构造方法,会将i的标记值变为1,j的标记值变为1.
    // 然后执行int i = 5,舍弃i的标记值1,i赋值为5
    // 然后执行 int j;由于j没有初始化值,所以将标记值1赋值给j。
//     所以结果是 5 1
    static SDemo sd = new SDemo();
    static int i = 5;
    static int j;
    public SDemo(){
        i++;
        j++;
    }
}

1 final

final 是 java 中的一个关键字,可以修饰数据,方法和类

1.1 final修饰数据

final修饰的数据称之为常量,定义之后不能修改。final修饰基本数据类型,其值不能修改。final修饰引用数据类型,其地址不能修改。

  • final作为常量不能修改
// i目前被final修饰,这个值就是常量,不能修改
        final int i;
        i = 8;
  • final的常量可以作为参数传入方法中,在方法中可以修改
    public static void main(String[] args) {
        // i目前被final修饰,这个值就是常量,不能修改
        final int i;
        i = 8;
        add(i);

        System.out.println(i);
    }

    // final作为常量参数传入方法,这个参数可以改变。但是原来的实参的值并没有发生改变
    public static void add(int i){
        i++;
        System.out.println(i);
    }
  • final修饰引用数据类型,地址不能改变
// final修饰的对象的地址不能改变
        final int[] arr = {3,4,5};
        System.out.println(arr);
// 数组里面的值是可以改变的
        arr[0] = 8;
        System.out.println(Arrays.toString(arr));
        arr = new int[3];
  • final修饰的引用数据类型可以作为参数传入方法中,在方法中可以改变地址。
        // final修饰的对象的地址不能改变
        final int[] arr = {3,4,5};
        System.out.println(arr);
//        arr[0] = 8;
//        System.out.println(Arrays.toString(arr));
//        arr = new int[3];
        // 能不能扩容? 不能扩容,因为扩容也是创建一个新的数组,地址还是会发生改变
//        arr = Arrays.copyOf(arr,arr.length * 2);

        changeRef(arr);
        System.out.println(arr);
    }

    public static void changeRef(int[] arr1){
        arr1 = Arrays.copyOf(arr1,arr1.length * 2);
    }
  • final可以修饰形参,因为形参本质上就是局部变量,下面的写法会报错
        // final修饰的对象的地址不能改变
        final int[] arr = {3,4,5};
        System.out.println(arr);
//        arr[0] = 8;
//        System.out.println(Arrays.toString(arr));
//        arr = new int[3];
        // 能不能扩容? 不能扩容,因为扩容也是创建一个新的数组,地址还是会发生改变
//        arr = Arrays.copyOf(arr,arr.length * 2);

        changeRef(arr);
        System.out.println(arr);
    }

    public static void changeRef(final int[] arr1){
        // 这里是要给final修饰的arr1的地址改变,所以不行
        arr1 = Arrays.copyOf(arr1,arr1.length * 2);
    }
  • final修饰的成员变量需要在对象创建完成前赋值。
class Student{
    // 成员变量在堆中,有默认值.如果编译成功,i的值永远都是0。
    // final修饰的成员变量要求在对象创建完成之前给值
    final int i;
    int j;

    
    // 只能在构造代码块或者构造方法中赋值,不能同时赋值。因为同时赋值相当于要修改final修饰的常量的值。
//    {
//        i = 7;
//    }
    public Student(){
        this.i = 9;
    }
    
}


  • final修饰的静态变量称之为静态常量,要求在类加载完成之前赋值
class Person{
    // 静态常量要求在类加载完成之前给值
//    static final int i = 10;
    static final int i;
    static {
        i = 6;
    }
}

总结:final修饰的数据称之为常量,定义之后不能修改。对于基本数据类型而言指的是值不能改变,对于引用数据类型而言,指的是地址不能改变,其中的属性和元素是可以改变的。

1.2 final修饰方法

final修饰的方法称之为最终方法,不能被重写或者隐藏。

class Person{
    // 静态常量要求在类加载完成之前给值
//    static final int i = 10;
    static final int i;

    int age;

    String name;
    static {
        i = 6;
    }

    public final  void eat(){
        System.out.println("人要吃饭~");
    }
}

class Teacher extends Person{
    // 这里会报错,因为final修饰的方法不能被重写
    public void eat(){
        
    }
}
  • final修饰的方法可以被继承。
  • final修饰的方法可以被重载。
public final  void eat(){
        System.out.println("人要吃饭~");
    }
    
    public final void eat(int a){
        
    }

1.3 final修饰类

final修饰的类称之为最终类,不能被继承。

// 面试题:

  • String能不能被继承?

不能,因为String是被final修饰的最终类,不能被继承。

// 这种写法编译不通过
class SubString extends String{
    
}

2 abstract

abstract是一个关键字,可以修饰方法,也可以修饰类。修饰的方法没有方法体,称之为抽象方法。抽象方法只能存在于抽象类中。

  • 抽象方法只能存放在抽象类中(接口)。
public abstract void work();
  • 定义抽象类
abstract class Proffession{}
  • 抽象类不能直接创建对象,需要使用向上转型
Proffession proffession = new Teacher();
  • 抽象类能不能被final修饰?

不行。抽象类中的抽象方法必须要有类来实现,final修饰的类是最终类,不能被继承。

  • 抽象类中可以定义普通方法,也可以有构造方法

在这里插入图片描述

  • 抽象方法可以被重载。子类需要实现所有的抽象方法。

在这里插入图片描述

  • 抽象方法可以重载,必然要被继承和重写。那么不能用private/final/static修饰
  • 抽象类也是一个类,受到java单继承的限制。

3 接口

3.1 接口概述

接口是功能的集合。同样可以看作一种引用数据类型,是比抽象类更抽象的"类"。

接口一般只描述所应该具备的方法,并没有具体的实现。具体的实现由接口的实现类(类似于接口的子类)来完成。这样将功能的定义与实现分离,优化程序设计。

在JDK1.8之前,接口中的所有的方法都是抽象方法,从JDK1.8之后开始允许接口中存在静态方法和默认方法。

请记住:一切事物均有功能,即一切事物均有接口。

3.2 接口的基本使用

定义接口Profession

public interface Profession {
    // 接口中的抽象方法
    public abstract void work();
    public abstract double salary();
}

定义接口的实现类,实现类和接口之间使用implements关键字建立联系

public class Actor implements Profession{

    @Override
    public void work() {
        System.out.println("跳舞~");
    }

    @Override
    public double salary() {
        return 10000.0;
    }
}

测试

public static void main(String[] args) {
        // 接口也是一种引用数据类型  使用多态的向上转型来创建
        //  父接口 对象名 = new 接口的实现类();
        Proffession proffession = new Actor();
        proffession.work();
        System.out.println(proffession.salary());

    }

3.3 接口的特性

接口也是一个.java文件,编译后会产生.class文件.但是接口不是一个类。

  • 接口中的抽象方法只能被public abstract修饰,可以省略,在编译后会自动加上public abstract

  • 可以使用javap 这个指令来反编译,查看生成的.class文件

在这里插入图片描述

  • 接口中无法定义普通成员变量,必须使用固定写法。可以省略修饰符,编译时会自动加上。
public static final 数据类型 变量名 =;
  • 经常会定义一个接口,里面定义变量,实际上都是静态常量。

  • 在接口中修饰符可以写也可以不写,对于结果是一样的。

  • 一般接口的实现类命名都是 接口名+impl

  • 接口不能实例化,也没有构造方法

  • 需要使用向上转型来创建接口

父接口 接口名 = new 接口的实现类();

3.4 接口的多继承

在java中,接口和接口之间是多继承。多个接口之间用,隔开。


interface UserMapper{
    void a();
    void b();
}

interface AdminMapper{
    void c();
}

interface BrandMapper{
    void d();
}
// 接口的多继承 分别从接口中继承了 a b c d方法
interface MyMapper extends UserMapper,AdminMapper,BrandMapper{
    
}

// 需要实现四个方法
class MyClass implements MyMapper{

    @Override
    public void a() {
        
    }

    @Override
    public void b() {

    }

    @Override
    public void c() {

    }

    @Override
    public void d() {

    }
}

3.5 类和接口的多实现

一个类可以实现多个接口,用,隔开

class MyClassImpl implements UserMapper,AdminMapper{

    @Override
    public void a() {
        
    }

    @Override
    public void b() {

    }

    @Override
    public void c() {

    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值