Java中this和super、final和static、abstract和interface关键字的用法详细总结

一、this

this是代表当前对象的引用
我们会发现在构造函数的内部,我们可以使用this关键字,构造函数是用来构造对象的,对象还没有构造好,我们就使用了this,那this还代表当前对象吗?当然不是,this代表的是当前对象的引用。
一个对象的产生分为几步? ①为对象分配内存 ②调用合适的构造方法。(构造方法不止一个)那么既然对象产生分两步,必须把两步骤完成,第二步走完之后才会产生对象,而产生对象的过程中就用this,this就不能代表当前对象,因为只完成了第一步为对象分配内存,那么此时this就代表当前对象

    public Student(String myName, int age) {
        this.myName = myName;
        this.age = age;
    }

用法

  • this(); 调用本类其他的构造方法,且必须放在第一行
  • this.data; 访问当前类当中对属性
  • this.func(); 调用本类的其他的成员方法
class  Student {
    private String myName ;
    private int age;

    //默认构造函数 构造对象
    public Student( ) {
        this("Tom",12); //-----调用本类其它构造方法 必须放在第一行进行显示
    }

    public Student(String myName, int age) {
        this.myName = myName;
        this.age = age;
    }

    public  void  func1 () {
         this.show();//------------------调用本类的其他成员方法
        System.out.println("func1 ( )");
        System.out.println(this.age);//----------访问当前类当中的字段
    }

    public void show() {
        System.out.println("我的名字是" + this.myName + ", 今年" + this.age + "岁");
    }


    public void setMyName ( String myName) {
        this.myName = myName;  //---------- 正确this引用,表示调用该方法的对象
    }                                       //或者当前对象的引用

    public String getMyName ( ) {
        return this.myName;
    }
}

public class TestDemo {
    public static void main(String[] args) {
        Student student  = new Student( );
        student.show();  //我叫Tom, 今年12岁
        student.func1();
    }
}

二、super

super是代表父类对象的引用
为什么子类对象初始化时,都需要调用父类中的构造函数?为什么要在子类构造函数的第一行加入这个super()
因为子类继承父类,会继承到父类中的数据,所以必须要看父类是如何对自己的数据进行初始化的。所以子类在进行对象初始化时,先调用父类的构造函数,这就是子类的实例化过程。

  • super(); 调用父类的构造方法也是必须放到第一 行
  • super.data(); 访问父类的属性
  • super.func(); 访问父类的成员方法
  class Animal {
    protected String name;
    public Animal(String name) {
        this.name = name;
    }
    public void eat(String food) {
        System.out.println("我是一只小动物");
        System.out.println(this.name + "正在吃" + food);
    }
}

public class Bird extends Animal {
    public Bird(String name) {      
        super(name);     //调用父类的构造方法
    }
    @Override
    public void eat(String food) {
        super.eat(food);        //调用父类的方法
        System.out.println(super.name); //调用父类的属性
        System.out.println("我是一只小鸟");
        System.out.println(this.name + "正在吃" + food);
    }
}

总结

  • super(参数):调用基类中的某一个构造函数(应该为构造函数中的第一条语句)

  • this(参数):调用本类中另一种形成的构造函数(应该为构造函数中的第一条语句)

  • 调用super()必须写在子类构造方法的第一行,否则编译不通过。每个子类构造方法的第一条语句,都是隐含地调用 super(),如果父类没有这种形式的构造函数,那么在编译的时候就会报错。

  • super() 和 this() 均需放在构造方法内第一行。且不能同时出现在一个构造函数里

  • 尽管可以用this调用一个构造器,但却不能调用两个。

  • this() 和 super() 都指的是对象的引用,所以均不可以在 static 环境中使用。包括:static 变量,static 方法,static 语句块。

三、final

1、修饰变量:表示变量只能一次赋值以后值不能被修改(常量)
final成员变量表示常量,只能被赋值一次,赋值后值不再改变。

  • 当final修饰一个基本数据类型时,表示该基本数据类型的值一旦在初始化后便不能发生变化;
  • 如果final修饰一个引用类型时,则在对其初始化之后便不能再让其指向其他对象了,但该引用所指向的对象的内容是可以发生变化的。本质上是一回事,因为引用的值是一个地址,final要求值能变,即地址的值不发生变化
  • 当final修饰的是一个成员变量(属性),必须要显示初始化。这里有两种初始化方式,一种是在变量声明的时候初始化;第二种方法是在声明变量的时候不赋初值,但是要在这个变量所在的类的所有的构造函数中对这个变量赋初值
public class Test3 {
    public static final int a = 9; //初始化
    public static final List<Integer> list = new LinkedList<>();
    public static void main(String[] args) {
        list.add(1);
        list.add(2);
        list.add(3);
  //      list = new ArrayList<>(); //报错 因为list被final修饰 不能更改
        System.out.println(list); //正常打印 1 2 3 
    }
}

2、修饰类:表示该类不能被继承;
3、修饰方法:表示方法不能被重写;

final class Animal {  //-------------final修饰的类
    public String name;
    public Animal( ) {
    }
    public void eat(String food) {
        System.out.println("父类"+this.name + "正在吃" + food);
    }
    public void cc() {
        System.out.println("父类cc");
    }
}
class Cat extends Animal {  //-------------无法继承
    public Cat(String name) {
        System.out.println("子类"+this.name);
    }
    @Override 				//--------无法继承导致无法重写
    public void cc() {
        System.out.println("子类cc");
    }
}

4、final变量和普通变量的区别

  • 当final变量是基本数据类型以及String类型时,如果在编译期间能知道它的确切值,则编译器会把它当做编译期常量使用。也就是说在用到该final变量的地方,相当于直接访问的这个常量,不需要在运行时确定。
  • 由于变量b被final修饰,因此会被当做编译器常量,所以在使用到b的地方会直接将变量b 替换为它的值。而对于变量d的访问却需要在运行时通过链接来进行。
public class Test { 
    public static void main(String[] args)  { 
        String a = "hello2";   
        final String b = "hello"; 
        String d = "hello"; 
        String c = b + 2;   
        String e = d + 2; 
        System.out.println((a == c)); // true
        System.out.println((a == e)); // false
    } 
}

四、static

1、修饰成员属性:将其变为类的成员,从而实现所有对象对于该成员的共享
虽然两个Person1都和Person2都分别设置了自己的年龄分别为1、99,但是由于被static修饰的为静态成员变量,它只有一份,内存在方法区上,是属于类的属性,不属于对象。所以最后打印的都是是一个最后修改的值99。

  • 静态变量属于类,被所有对象共享,在内存中只有一个副本,在类初次加载的时候才会初始化,不依赖于对象的
  • 非静态变量是对象所拥有的,在创建对象的时候被初始化,存在多个副本,各个对象拥有的副本互不影响
  • static成员变量初始化顺序按照定义的顺序来进行初始化
public class Person {
        String name;
        static int age;  

        public String toString() {
            return "Name:" + name + ", Age:" + age;
        }
        public static void main(String[] args) {
            Person p1 = new Person();
            p1.name = "Tom";
            p1.age = 1;
            Person p2 = new Person();
            p2.name = "Cat";
            p2.age = 99;
            System.out.println(p1); // Name:Tom, Age:99
            System.out.println(p2);// Name:Cat, Age:99
        }
}

2、修饰成员方法:将其变为类方法,可以直接使用“类名.方法名”的方式调用,常用于工具类;
static修饰成员方法最大的作用,就是可以使用"类名.方法名"的方式操作方法,避免了先要new出对象的繁琐和资源消耗,比如main函数在调用其他非静态的方法就需要new对象

  • 静态方法中不能访问非静态成员方法和非静态成员变量
  • 非静态成员方法中是可以访问静态成员方法和静态成员变量
  • 不依赖于对象,通过 类名.方法名 调用
  • static方法是属于类的,非实例对象,也不能出现this、super;同时在JVM加载类时,就已经存在内存中,不会被虚拟机GC回收掉,这样内存负荷会很大,但是非static方法会在运行完毕后被虚拟机GC掉,减轻内存压力
public class Test3 {
    private   void function() {
        System.out.println("非常静态的方法需要new对象");
    }
    public static void main(String[] args) {
        Test3 test3 = new Test3();
        test3.function();
    }
}

3、静态块用法,将多个类成员放在一起初始化,使得程序更加规整,其中理解对象的初始化过程非常关键;

  • 构造方法用于对象的初始化。静态初始化块,用于类的初始化操作。
  • 在静态初始化块中不能直接访问非staic成员。
  • 静态初始化块的作用就是:提升程序性能。
  • 静态初始化块可以置于类中的任何地方,类中可以有多个静态初始化块。在类初次被加载时,会按照静态初始化块的顺序来执行每个块,并且只会执行一次。

执行顺序:

①静态代码块
②实例代码块
③构造方法

class Book {
    public Book(String msg) {
        System.out.println(msg);
    }
}

public class Person {

    Book book1 = new Book("book1成员变量初始化");
    static Book book2;

    static {
        book2 = new Book("static成员book2成员变量初始化");
        book4 = new Book("static成员book4成员变量初始化");
    }

    public Person(String msg) {
        System.out.println(msg);
    }

    Book book3 = new Book("book3成员变量初始化");
    static Book book4;

    public static void funStatic() {
        System.out.println("static修饰的funStatic方法");
    }

    public static void main(String[] args) {
        Person.funStatic();
        System.out.println("****************");
        Person p1 = new Person("p1初始化");
    }
    /**Output
     * static成员book2成员变量初始化
     * static成员book4成员变量初始化
     * static修饰的funStatic方法
     * ***************
     * book1成员变量初始化
     * book3成员变量初始化
     * p1初始化
     */
}

五、抽象类和接口的异同点

不同点:

  • 抽象类子类使用extends关键字来继承抽象类,接口实现类使用关键字implements来实现接口;
  • 抽象类中除了抽象方法外,可以包含普通类的字段和方法;接口中方法只能是public abstract,字段只能是 public static final
  • 抽象类可以有构造方法,接口中不能有构造方法。
  • 抽象类中可以有普通成员变量,接口中没有普通成员变量
  • 抽象类中可以包含静态方法,接口中不能包含静态方法
  • 接口可以被多重实现,抽象类只能被单一继承
  • 如果抽象类或者普通类实现了接口,则可以把接口中方法映射到抽象类中作为抽象方法而不必实现,而在抽象类或者普通类的子类中实现接口中方法
  • 抽象类或者抽象方法 一定是不能被final,static,private 修饰的(重写不支持那三个)

相同点:

  • 都不能被实例化
  • 派生类必须重写未实现的方法

例子:
门和警报的例子:门都有open( )和close( )两个动作,此时我们可以定义通过抽象类或者接口来定义这个抽象概念:

abstract class Door {
    public abstract void open();
    public abstract void close();
}
或者---------------------------
interface Door {
    public abstract void open();
    public abstract void close();
}

但是现在如果我们需要门具有报警alarm( )的功能,那么该如何实现?下面提供两种思路:

①将这三个功能都放在抽象类里面,但是这样一来所有继承于这个抽象类的子类都具备了报警功能,但是有的门并不一定具备报警功能;

②将这三个功能都放在接口里面,需要用到报警功能的类就需要实现这个接口中的open( )和close( ),也许这个类根本就不具备open( )和close( )这两个功能,比如火灾报警器。

从这里可以看出, Door的open() 、close()和alarm()根本就属于两个不同范畴内的行为,open()和close()属于门本身固有的行为特性,而alarm()属于延伸的附加行为。因此最好的解决办法是单独将报警设计为一个接口,包含alarm()行为,Door设计为单独的一个抽象类,包含open和close两种行为。再设计一个报警门继承Door类和实现Alarm接口。

interface Alram {
    void alarm();
}
abstract class Door {
    void open();
    void close();
}
class AlarmDoor extends Door implements Alarm {
    void oepn() {
      //....
    }
    void close() {
      //....
    }
    void alarm() {
      //....
    }
}
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值