Java学习-关键字Static和Final

static是java中非常重要的一个关键字,而且它的用法也很丰富,主要有四种用法:

  • 用来修饰成员变量,将其变为类的成员,从而实现所有对象对于该成员的共享;
  • 用来修饰成员方法,将其变为类方法,可以直接使用“类名.方法名”的方式调用,常用于工具类;
  • 静态块用法,将多个类成员放在一起初始化,使得程序更加规整,其中理解对象的初始化过程非常关键;
  • 静态导包用法,将类的方法直接导入到当前类中,从而直接使用“方法名”即可调用类方法,更加方便;
  • 简单实例
public class Person {
	int age;
	String name;
	String nation;
	
	public void ShowIdentity() {
		System.out.println("姓名:" + name + " 年龄: " + age + " 国籍:" + nation);
	}
}

public class show {

	public static void main(String[] args) {
		Person CHN = new Person();
		CHN.name = "李华";
		CHN.age = 20;
		CHN.nation = "中国";
		
		Person USA = new Person();
		USA.name = "Jack";
		USA.age = 18;
		USA.nation = "美国";

		CHN.ShowIdentity();
		USA.ShowIdentity();

	}
}
console结果:
姓名:李华 年龄: 20 国籍:中国
姓名:Jack 年龄: 18 国籍:美国

使用Static后

//
public class Person {
	int age;
	String name;
	static String nation;//静态成员变量
	
	public void ShowIdentity() {
		System.out.println("姓名:" + name + " 年龄: " + age + " 国籍:" + nation);
	}
}
//
public class show {

	public static void main(String[] args) {
		Person CHN = new Person();
		CHN.name = "李华";
		CHN.age = 20;
		Person.nation = "中国";
		
		Person USA = new Person();
		USA.name = "Jack";
		USA.age = 18;
		Person.nation = "美国";
		
		CHN.ShowIdentity();
		USA.ShowIdentity();
	}
}
console结果:
姓名:李华 年龄: 20 国籍:美国
姓名:Jack 年龄: 18 国籍:美国

内存模型:
数据存储
过程:
1.Java源代码文件(.java后缀)会被Java编译器编译为字节码文件(.class后缀);
2.JVM中的类加载器将各个类的字节码文件加载到方法区,其中类中的静态成员变量会放到“静态存储区”;
3.加载完成后,JVM执行引擎开始执行程序;

1.修饰成员变量
特点:被static修饰的成员变量和成员方法独立于该类的任何对象。也就是说,它不依赖类特定的实例,被类的所有实例共享;
格式:类名.静态变量名 (不推荐使用 对象.静态变量名);
优点:1.对象之间可以共享属性;2.类名直接调用更方便;
不足:只分配一次内存,只有一个拷贝,不够灵活;

2.修饰成员方法
特点:修饰成员方法对于数据的存储上面并没有多大的变化,方法本来就是存放在类的定义当中的;
格式:类名.静态方法名;
优点:方便方法的调用,避免了先要new出对象的繁琐和资源消耗;父类中的静态方法不会被子类的重写覆盖;
不足:静态方法中不能用this和super关键字(因为调用静态方法没有调用对象,直接访问的方法区);不能直接访问所属类的非static的成员变量和成员成员方法,只能访问所属类的静态成员变量和成员方法;(因为每创建一次实例产生对象,实例变量就会产生一个拷贝,如果允许直接访问non-static filed,调用static方法时程序无法判断去使用哪个对象中的哪个变量,所以必须只可以访问static filed);

3.静态块
特点:一 般情况下,如果有些代码必须在项目启动的时候就执行的时候,这时可以使用静态代码块,这种代码是主动执行的;
格式:static {…//需要执行的代码}
示例

//普通类
public class PuTong {
	public PuTong(){
		System.out.print("默认构造方法!-->");
	}
	//非静态代码块
	{
		System.out.print("非静态代码块!-->");
	}
	//静态代码块
	static{
		System.out.print("静态代码块!-->");
	}

	public static void test(){
		System.out.println("普通方法中的代码块!");
	{
}

//测试
public class TestClass {
public static void main(String[] args) {
PuTong c1 = new PuTong();
c1.test();

PuTong c2 = new PuTong();
c2.test();
}
}

/*
运行输出结果是:
静态代码块!-->非静态代码块!-->默认构造方法!-->普通方法中的代码块!
非静态代码块!-->默认构造方法!-->普通方法中的代码块!
*/

区别
1.静态代码块:只在第一次创建对象时执行一次,而且是第一个执行;
2.非静态代码块:每创建一次对象,就执行一次;
注意

对象的初始化顺序:首先执行父类静态的内容,父类静态的内容执行完毕后,接着去执行子类的静态的内容,当子类的静态内容执行完毕之后,再去看父类有没有非静态代码块,如果有就执行父类的非静态代码块,父类的非静态代码块执行完毕,接着执行父类的构造方法;父类的构造方法执行完毕之后,它接着去看子类有没有非静态代码块,如果有就执行子类的非静态代码块。子类的非静态代码块执行完毕再去执行子类的构造方法。总之一句话,静态代码块内容先执行,接着执行父类非静态代码块和构造方法,然后执行子类非静态代码块和构造方法。而且子类的构造方法,不管这个构造方法带不带参数,默认的它都会先去寻找父类的不带参数的构造方法。如果父类没有不带参数的构造方法,那么子类必须用supper关键子来调用父类带参数的构造方法,否则编译不能通过。

4.静态导包

5.典型应用-单例模式:
单例模式(Singleton模式) ,指的是一个类,在一个JVM里,只有一个实例存在。

  • 饿汉式单例模式
    示例:
public class Person {
	private int age; 
	
	public int getAge() {
		return age;
	}
	//1.创建一个私有类型的构造,禁止外部创建实例对象
	private Person(int age) {
		this.age = age;
	}
	//2.创建静态类型的引用(p),指向一个实例化对象,同时私有化禁止外部调用
	private static Person p = new Person(18);
	//3.创建静态类型的get方法,返回出去唯一的实例对象
	public static Person getInstance() {
		return p;
	}
}

public class show {

	public static void main(String[] args) {
		Person p1 = Person.getInstance();
		Person p2 = Person.getInstance();
		
		System.out.println(p1.getAge() + ":" + p2.getAge());
		System.out.println(p1+ ":" + p2);
		
	}
}
console结果:
18:18
temp.Person@15db9742:temp.Person@15db9742
  • 分析
    在这里插入图片描述
    1.内部私有化构造,外部无法创建新的实例;
    2.内部静态类型的引用指向创建的唯一一个实例对象;
    3.内部创建静态方法并返回上一步p指向的实例对象,方便外部通过唯一的入口进行调用;

懒汉式单例模式

  • 示例
public class Person {
	private int age; 
	
	public int getAge() {
		return age;
	}
	//创建一个私有类型的构造,禁止外部创建实例对象
	private Person(int age) {
		this.age = age;
	}
	//创建静态类型的引用(p),默认指向null
	private static Person p;
	//创建public静态方法,返回实例对象
	public static Person getInstance(){
	        //第一次访问的时候,发现p没有指向任何对象,这时才会实例化一个对象
	        if(null==p){
	            p = new Person(18);
	        }
	        return p;  //返回 p指向的对象
	    }
}

public class show {

	public static void main(String[] args) {
		Person p1 = Person.getInstance();
		Person p2 = Person.getInstance();
		
		System.out.println(p1.getAge() + ":" + p2.getAge());
		System.out.println(p1+ ":" + p2);
		
	}
}
console结果:
18:18
temp.Person@15db9742:temp.Person@15db9742

饿汉式单例模式:是立即加载的方式,无论是否会用到这个对象,都会加载。 如果在构造方法里写了性能消耗较大,占时较久的代码,比如建立与数据库的连接,那么就会在启动的时候感觉稍微有些卡顿。

懒汉式单例模式:是延迟加载的方式,只有使用的时候才会加载。 并且有线程安全的考量(鉴于同学们学习的进度,暂时不对线程的章节做展开)。使用懒汉式,在启动的时候,会感觉到比饿汉式略快,因为并没有做对象的实例化。但是在第一次调用的时候,会进行实例化操作,感觉上就略慢。
何时使用:如果业务上允许有比较充分的启动和初始化时间,就使用饿汉式,否则就使用懒汉式


final关键字是我们经常使用的关键字之一,它的用法有很多,但是并不是每一种用法都值得我们去广泛使用。它的主要用法有以下四种:

  • 用来修饰成员变量,我们必须在声明时或者构造方法中对它赋值,该变量只能被赋值一次且它的值无法被改变;
  • 用来修饰局部变量,该变量只能被赋值一次且它的值无法被改变;
  • 修饰方法,表示该方法无法被重写;
  • 修饰类,表示该类无法被继承;
    第三种和第四种方法需要谨慎使用,因为在大多数情况下,如果是仅仅为了一点设计上的考虑,我们并不需要使用final来修饰方法和类。

1.修饰类
特点:当用final修饰一个类时,表明这个类不能被继承,final类中的成员变量可以根据需要设为final,但是要注意final类中的所有成员方法都会被隐式地指定为final方法;
格式:final class 类名{}
使用时机:这个类在以后不会用来继承或者出于安全的考虑,可以考虑使用;
在这里插入图片描述

2.修饰方法
特点:当用final修饰成员方法时,该方法不能被子类重写(覆盖);
格式:例:public final void xxx(){}
使用时机:将方法锁定,以防任何继承类修改它的含义
在这里插入图片描述

3.修饰成员变量
特点:final作用于类的成员变量时,成员变量(注意是类的成员变量,局部变量只需要保证在使用之前被初始化赋值即可)必须在声明时或者构造器中进行初始化赋值,而且final变量一旦被初始化赋值之后,就不能再被赋值了。
在这里插入图片描述


在这里插入图片描述
4.修饰局部变量
特点:对于一个final变量,如果是基本数据类型的变量,则其数值一旦在初始化之后便不能更改;如果是引用类型的变量,则在对其初始化之后便不能再让其指向另一个对象。

修饰引用类型变量分析:
在这里插入图片描述
内存模型:
在这里插入图片描述


总结:
很多时候会容易把static和final关键字混淆,简单来说static作用于成员变量用来表示只保存一份副本,而final的作用是用来保证变量不可变;

public class Test {
	final double i = Math.random();
	static double j = Math.random();
	
	public void Print() {
		System.out.println("hello");
	}
}

public class show {

	public static void main(String[] args) {

		final Test tt = new Test();
		System.out.println("i:" + tt.i + "---j:" + Test.j);
		
		Test ee = new Test();
		System.out.println("i:" + ee.i + "---j:" + Test.j);

	}
}
console第一次运行结果:
i:0.43682430830851604---j:0.2186265152944704
i:0.12239426737840786---j:0.2186265152944704
console第二次运行结果:
i:0.040292758906855575---j:0.695015407609792
i:0.2527122814442675---j:0.695015407609792

每次运行都是j结果相同,i不相同,static和final区别显而易见;

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值