static&this&super

static关键字

static关键字用来在类中修饰成员变量和方法,但是不能修饰局部变量,static修饰后的成员变量和方法可以在不用创建对象的情况下直接调用,但是不能在static方法中调用没有static修饰的方法的和变量。

总之static的用途就是一句话:“方便在没有创建对象的情况下来进行调用方法/变量”。可以直接使用类名来调用类变量,比如:System.out.println("直接使用类名调用类变量:“"+Test2.apartment);

static关键字的加载流程

本博客主要参考:Java中的static关键字解析

static变量也称作静态变量,静态变量和非静态变量的区别是:静态变量被所有的对象所共享,在内存中只有一个副本,它当且仅当在类初次加载时会被初始化,不属于任何对象,只属于这个类,主调是类名。

而非静态变量是对象所拥有的,在创建对象的时候被初始化,存在多个副本,各个对象拥有的副本互不影响。

上面这两句话什么意思,通过一段程序来说明:没有创建对象,运行一切正常。

class NullAccesssstatic
{
	public static void test(){
		System.out.println("static 修饰的类方法");
	}
}

public class Test3
{
	public static void main(String args[]){
		NullAccesssstatic a=null;
		a.test();
	}
}



public class Student {
    private String name;
    private String sex;
    private static String apartment;

    public Student(String name,String sex,String apart){
        this.name = name;
        this.sex = sex;
        apartment = apart;
    }

    public String getName(){
        return this.name;
    }

    public String getSex(){
        return this.sex;
    }

    public String getApartment(){
        return apartment;
    }

    public static void main(String[] args){
        Student student = new Student("朱鹏程","男","信科院");
        Student student1 = new Student("周圣菲","女","国际学院");
        System.out.println(student.getName()+" "+student.getApartment());
        System.out.println(student1.getName()+" "+student1.getApartment());
    }
}

输出结果:

朱鹏程 国际学院
周圣菲 国际学院


可以看到static修饰的变量会被后来的结果给覆盖掉,而非static修饰的变量则不会。由此建议当程序访问类变量时,尽量使用类作为主调,而不要使用对象作为主调,这样可以避免程序出产生的歧义,提高程序的可读性《java疯狂讲义·》。

参考:static共享内存

这样看来main对象为什么是static修饰的就显而易见了。因为main对象没有被任何对象调用,只能通过加载类的时候直接加载。

常见的面试问题:

参考:https://www.cnblogs.com/dolphin0520/p/3799052.html

1.下面的代码的输出结果

public class Test extends Base{
 
    static{
        System.out.println("test static");
    }
     
    public Test(){
        System.out.println("test constructor");
    }
     
    public static void main(String[] args) {
        new Test();
    }
}
 
class Base{
     
    static{
        System.out.println("base static");
    }
     
    public Base(){
        System.out.println("base constructor");
    }
}

输出:

base static
test static
base constructor
test constructor

代码的执行过程:

代码执行开始首先会加载Test类,发现Text类还有父类,转去加载Test的父类Base,加载Base的过程会初始化static修饰代码块,输出base static,然后加载Test类,执行Test类的中staic修饰的代码块,输出test static,继续执行static修饰的main方法,实例化Test的类的过程会先调用父类的构造器然后再调用Test自身的构造器。

public class Test {
    Person person = new Person("Test");
    static{
        System.out.println("test static");
    }
     
    public Test() {
        System.out.println("test constructor");
    }
     
    public static void main(String[] args) {
        new MyClass();
    }
}
 
class Person{
    static{
        System.out.println("person static");
    }
    public Person(String str) {
        System.out.println("person "+str);
    }
}
 
 
class MyClass extends Test {
    Person person = new Person("MyClass");
    static{
        System.out.println("myclass static");
    }
     
    public MyClass() {
        System.out.println("myclass constructor");
    }
}

输出:

test static
myclass static
person static
person Test
test constructor
person MyClass
myclass constructor


代码的执行过程:

首先根据类名加载Test类,发现类里面有static代码块,进行初始化,输出test static,然后执行main方法,main方法里面进行实例化里一个MyClass,因为MyClass的父类Test已经被加载完毕了,所以只需加载MyClass类即可,加载类的过程就要执行static块,输出myclass static,加载完成之后,就要调用构造器生成生成对象,而在生成对象的钱要对父类的成员变量进行初始化,这个时候就要对Person类进行实例化,因为person类还没有被加载,所以需要先加载person类,执行类中的static块,输出person static,然后调用构造器生成对象,输出person Test,然后回到Test类执行父类的构造方法,输出test constructor,然后再对MyClass类的成员变量进行初始化,输出person MyClass,最后调用myclass的构造的方法。(先加载后实例,父先子后)

static关键字可以用来优化程序

static的另一个关键作用就是可以用来形成静态代码块来优化程序性能,因为被static修饰的对象只在类加载的时候执行一次。

比如以下代码,每次调用isBornBoomer的时候就会创建两个Date对象,对象的创建是比较耗费资源的 。

class Person{
    private Date birthDate;
     
    public Person(Date birthDate) {
        this.birthDate = birthDate;
    }
     
    boolean isBornBoomer() {
        Date startDate = Date.valueOf("1946");
        Date endDate = Date.valueOf("1964");
        return birthDate.compareTo(startDate)>=0 && birthDate.compareTo(endDate) < 0;
    }
}

如果将对象Date设置为静态的形式,那么在就只会在类加载的时候创建一次,这样效率会更高。

class Person{
    private Date birthDate;
    private static Date startDate,endDate;
    static{
        startDate = Date.valueOf("1946");
        endDate = Date.valueOf("1964");
    }
     
    public Person(Date birthDate) {
        this.birthDate = birthDate;
    }
     
    boolean isBornBoomer() {
        return birthDate.compareTo(startDate)>=0 && birthDate.compareTo(endDate) < 0;
    }
}

这个时候可能会思考,为什么static可以修饰成员变量和方法,但是不能修饰局部变量呢?

因为没有意义

1.在非static方法中使用static修饰局部变量,因为在方法执行过程中,变量名会被放倒内存栈里面,方法执行结束之后内存栈就会被销毁,这个时候static也就是失去他应该有的意义。

2.如果在static修饰方法中,整个方法在加载时候类的时候就会被加载一次,方法里面的局部变量本来就是static的,更没有必要了。

参考:https://www.13419.com/j2se/178932.html

this关键字

当我们在非静态方法中想要调用同一个类里面的其他方法的时候怎么办的?你可能会说很简单啊,直接在方法中引用另一个函数的函数名就好了啊,但是如果用面向对象的思想来看这个问题,面向对象的世界里,主谓宾的结构完全成立,调用成员变量,方法时主调时必不可少,当我们调用static修饰的成员变量时省略了前面的主调,默认该类就是主调。当我们调用普通成员变量和方法时,默认省略了当前对象作为主调。

下面的第一段代码,在方法当中又重新创建了一个对象,然后调用,这个是没有必要的,因为当程序使用run方法的时候,肯定是需要一个Dog对象来进行调用的,我们可以直接使用这个已经存在的对象,而无需重新创建,当this出现在一个方法体的时候,他所对应的对象是不确定的,但是它的类型时确定,也就是说在this所在的方法没有被调用之前他相当于 Dog this;当这个方法被调用之后,this这个变量就会指向堆内存中的一个对象实例。

static修饰的方法时无法使用this,因为这个方法是一个类方法,它属于一个类,它的主调是一个类,而不是一个对象,主语都不一样,当然不可以了。

 

public class Dog{

    public class run(){

    Dog dog=new Dog();
    dog.run()

    }
}


public class Dog{

    public class run(){
    this.run()

    }
}

public class Dog{

    public class run(){
    run()

    }
}

此外this关键字还可以用来多个构造方法的情况下,一个构造方法调用另外一个构造方法。在软件工程中有一个规则不能把相同的代码写两次以上,如果两个构造器中有代码重复,并不能像调用普通方法那样直接用this.方法名()调用,因为构造器的只能通过new关键字来进行调用,但是这样的话就会创建一个对象,所以java采用直接使用this(参数)这样的方式来进行调用。

但是不能在普通方法中使用this(),否则会报错。

ThisConstructor.java:24: 错误: 对this的调用必须是构造器中的第一个语句    this("cccc","green");

import static java.lang.System.*;

public class ThisConstructor
{
	private String name;
	private String color;
	private double weight;
	public ThisConstructor(String name,String color){
		this.name=name;
		this.color=color;


	}

	public ThisConstructor(String name,String color,double weight){
	
		this(name,color);
		this.weight=weight;

	}

	public static void main(String args[]){
		ThisConstructor apple1=new ThisConstructor("aaaa","red");

		ThisConstructor apple2=new ThisConstructor("bbb","yellow",23.4);
	}

说到构造器的调用就再说说子类调用父类中的关键字,super限定。super调用必须出现在构造器的第一行。使用this调用另外一个构造器的时候,也要出现构造器的第一行,所以this和super不能同时出现。

不管子类构造器中使用使用super()调用父类构造器,系统都会调用父类构造器。在不是用super的条件下,系统会在子类构造器执行之前,先看看子类构造器的第一行,如果第一行没有super()向父类构造器传递参数,系统就会直接调用父类中的构造器,如果发现使用的super显示的调用父类中的构造器,系统将根据实参调用里传入的实参列表调用父类对应的构造器,并且将对应参数传递给父类。

此外是super还有另外一个用处,也是和this类似,他可以调用父类中被隐含的方法和实例变量,使用super.方法名/实例名。为什么这些实例和方法会被隐含呢,因为啊,当子类中重写了父类中的方法和实例变量,在子类中执行程序的时候回优先使用子类中的变量和方法,父类的就被隐含起来了。注意是隐含起来了,不是消失了,在创建子类的时候,系统为这个对象分配两个内存,子类对象一块,父类对象一块。

顺带说一句子类,父类和对象这三个困扰我许久的哲学关系,如果父类是人类,子类就是男人,对象就是睡在你旁的这个男人。如果男人是父类,子类就是渣男,对象就是你眼前的这个渣男,可以理解对象是真真实实存的东西。


import static java.lang.System.*;
class Creature
{
	public Creature(){
		out.println("这是Creature中没有参数的构造器");
	}
}
class Animal extends Creature
{
	String name;
	int age;
	public Animal(String name){
		this.name=name;
		out.println("这是Animal中含有一个参数name的构造器,name="+this.name);
	}
	public Animal(String name,int age){
		this(name);
		this.age=age;
		out.println("这是Animal中含有两个参数age和name的构造器,name="+this.name+"age="+this.age);
	}

}

public class  Wolf extends Animal
{
	public Wolf ()
	{
		super("灰太狼",123);
		out.println("这是Wolf类中的无参数构造器");
	}
	public static void main(String args[]){
		new TestConstru();
	}
}

这是Creature中没有参数的构造器
这是Animal中含有一个参数name的构造器,name=灰太狼
这是Animal中含有两个参数age和name的构造器,name=灰太狼age=123
这是Wolf类中的无参数构造器

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值