• 0025 Java学习笔记-面向对象-final修饰符、不可变类


    final关键字可以用于何处

    • 修饰类:该类不可被继承
    • 修饰变量:该变量一经初始化就不能被重新赋值,即使该值跟初始化的值相同或者指向同一个对象,也不可以
      • 类变量:
      • 实例变量:
      • 形参: 注意可以修饰形参
      • 局部变量
    • 修饰方法:该方法不可被重写

    final修饰成员变量

    • final修饰成员变量,必须显式的指定初始值,系统不会为final成员进行隐式初始化,不能在初始化前访问。
      • 因为,不管是类变量还是实例变量,都有个初始化的过程,初始化赋值后便不能再赋值,如果不显式的指定初始值,那么这些变量就没有存在的意义
    • 修饰类变量:
      • 要么声明时指定
      • 要么在静态块中指定
      • 二者互斥,只能是其一
    • 修饰实例变量:
      • 要么声明时指定
      • 要么非静态块中指定
      • 要么构造方法中指定
      • 三者任意两者互斥,只能是这三者之其一

    final修饰局部变量

    • 局部变量,不管有没有final修饰,都是必须显式初始化的
    • final修饰的形参,是不能赋值的

    final修饰引用类型变量

    • final可以用来修饰一个引用类型变量,但只能保证这个变量始终指向同一地址
    • 不能保证指向的对象本身发生改变

    final与直接量

    • 一个变量,不管是类变量、实例变量、局部变量,满足下面三个条件,便是一个直接量
      • 用final修饰
      • 定义该final变量时指定了初始值。注意是定义时,不能在代码块中或者构造方法中
      • 该初始值可以在编译时就被确定下来
    • 对于直接量,在编译的时候,会用直接量把变量名替换掉,编译后的文件中是不存在这个变量的
    • Java用常量池来管理使用过的字符串直接量

    final修饰方法

    • final修饰的方法不能被重写,比如作为根父类的Object就有一个.getClass()方法是用final修饰的
    • 如果父类有一个private final修饰的方法,子类也有这个方法,private final修饰,方法签名也相同,注意这不是重写,这是两个无关的方法,

    final修饰类

    • final修饰的类不能被继承,比如:
    • public final class Math extends Object {...}

    不可变(immutable)类

    • 不可变类:创建实例后,实例变量不可变,即实例的状态不能发生改变
    • 8个包装类和String都是不可变类
    • 如何定义一个不可变类:
      • 用private和final修饰成员变量
      • 提供带参数的构造器,用来初始化成员变量
      • 只提供成员变量的get方法,不提供set方法
      • 有必要的话重写equals()和hashCode()方法
    • 如果成员变量是引用类型
      • 这种情况要特别注意,上面的写法只能用于基本类型
      • 对引用类型不可变的写法,基本原则就是隔绝外部对它的访问,主要要做好两点:
        • 传递进来的引用类型对象不要直接用,而要根据其实例变量重新创建一个
        • 外部访问时,根据这个对象的实例变量的值,重新创建一个对象返回
      • 看下面的示例代码:

    示例一:该不可变类对引用类型变量无效

    public class Test{  
        public static void main(String[] args) { 
        	Name n=new Name("师兄","大");
        	System.out.println(n);            //Name@15db9742
        	Person p=new Person(n,100);
        	System.out.println(p);            //输出:[大师兄100岁]
        	System.out.println(p.getName());  //Name@15db9742
        	n.setFirstName("师兄");
        	n.setLastName("二");
        	System.out.println(p);            //输出:[二师兄100岁],不可变对象还是变了,再看下面一段代码
        	System.out.println(p.getName());  //Name@15db9742
        }
    }
    class Person{
    	private final Name name;
    	private final int age;
    	public Person(Name name,int age){
    		this.name=name;
    		this.age=age;
    	}
    	public Name getName(){
    		return name;
    	}
    	public String toString(){
    		return "["+name.getLastName()+name.getFirstName()+age+"岁]";
    	}
    }
    class Name{
    	private String firstName;
    	private String lastName;
    	public Name(){}
    	public Name(String first,String last){
    		firstName=first;
    		lastName=last;
    	}
    	public String getFirstName(){
    		return firstName;
    	}
    	public void setFirstName(String firstName){
    		this.firstName=firstName;
    	}
    	public String getLastName(){
    		return lastName;
    	}
    	public void setLastName(String lastName){
    		this.lastName=lastName;
    	}
    }
    

    示例代码二:改写了两行

    public class Test{  
        public static void main(String[] args) { 
        	Name n=new Name("师兄","大");
        	System.out.println(n);            //Name@15db9742
        	Person p=new Person(n,100);
        	System.out.println(p);            //输出:[大师兄100岁]
        	System.out.println(p.getName());  //Name@6d06d69c
        	n.setFirstName("师兄");
        	n.setLastName("二");
        	System.out.println(p);            //输出:[大师兄100岁]。没发生改变
        	System.out.println(p.getName());  //Name@7852e922
        }
    }
    class Person{
    	private final Name name;
    	private final int age;
    	public Person(Name name,int age){
    		this.name=new Name(name.getFirstName(),name.getLastName());  //此行改写
    		this.age=age;
    	}
    	public Name getName(){
    		return new Name(name.getFirstName(),name.getLastName());     //此行改写
    	}
    	public String toString(){
    		return "["+name.getLastName()+name.getFirstName()+age+"岁]";
    	}
    }
    class Name{
    	private String firstName;
    	private String lastName;
    	public Name(){}
    	public Name(String first,String last){
    		firstName=first;
    		lastName=last;
    	}
    	public String getFirstName(){
    		return firstName;
    	}
    	public void setFirstName(String firstName){
    		this.firstName=firstName;
    	}
    	public String getLastName(){
    		return lastName;
    	}
    	public void setLastName(String lastName){
    		this.lastName=lastName;
    	}
    }
    

    缓存实例的不可变类

    public class T1{  
        public static void main(String[] args) { 
        	CacheImmutable c1=CacheImmutable.valueOf("A");
        	CacheImmutable c2=CacheImmutable.valueOf("A");
        	System.out.println(c1==c2);                      //返回true
        }
    }
    class CacheImmutable{
    	private static int MAX_SIZE = 10;                    //缓存的大小
    	private static CacheImmutable[] cache=new CacheImmutable[MAX_SIZE]; //静态变量
    	private static int pos=0;                            //下一个要缓存的对象在cache中的索引号
    	private final String name;                           //CacheImmutable就这一个实例变量,用private final修饰,name是String类型,也是不可变的
    	private CacheImmutable(String name){                 //通过带参数的构造器初始化final的实例变量,但不允许外部通过它创建对象
    		this.name=name;
    	}
    	public String getName(){                             //提供了get方法,没提供set方法,
    		return name;
    	}
    	public static CacheImmutable valueOf(String name){   //要获得该类的对象,只能通过这个静态方法
    		for (int i=0;i<MAX_SIZE;i++){                    //先遍历一遍,看缓存中是否已经有了name对应的对象
    			if (cache[i]!=null && cache[i].getName().equals(name)){  //?为何不直接访问name,有何区别
    				return cache[i];                         //如果已经有了就将其返回,方法调用结束
    			}
    		}
    		if (pos==MAX_SIZE){
    			cache[0]=new CacheImmutable(name);           //如果已经缓存了10个(cache索引号9),那就从头开始覆盖
    			pos=1;
    		}else{
    			cache[pos]=new CacheImmutable(name);          //如果还不到10个,那就创建个对象,放在pos位置
    			pos++;
    		}
    		return cache[pos-1];                              //返回新创建并缓存的对象,方法调用结束
    	}
    	public boolean equals(Object obj){
    		if (this==obj) {
    			return true;
    		}
    		if (obj!=null && obj.getClass()==CacheImmutable.class) {
    			CacheImmutable ci=(CacheImmutable)obj;
    			return name.equals(ci.getName());
    		}
    		return false;
    	}
    	public int hashCode(){
    		return name.hashCode();
    	}
    }
    
  • 相关阅读:
    看我如何破解一台自动售货机
    充满未来和科幻的界面设计FUI在国内还没有起步在国外早起相当成熟
    libuv之介绍
    纯净版xp系统在局域网共享需要密码如何解决
    xp局域网共享设置
    Qt5设置应用程序图标
    我们为什么以及是如何从 Angular.js 迁移到 Vue.js?
    [转]聊聊技术选型
    VueJS如何引入css或者less文件的一些坑
    tomcat启动不成功(点击startup.bat闪退)的解决办法
  • 原文地址:https://www.cnblogs.com/sonng/p/6078735.html
Copyright © 2020-2023  润新知