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();
}
}

0025 Java学习笔记-面向对象-final修饰符、不可变类的更多相关文章

  1. 0013 Java学习笔记-面向对象-static、静态变量、静态方法、静态块、单例类

    static可以修饰哪些成员 成员变量---可以修饰 构造方法---不可以 方法---可以修饰 初始化块---可以修饰 内部类(包括接口.枚举)---可以修饰 总的来说:静态成员不能访问非静态成员 静 ...

  2. 0028 Java学习笔记-面向对象-Lambda表达式

    匿名内部类与Lambda表达式示例 下面代码来源于:0027 Java学习笔记-面向对象-(非静态.静态.局部.匿名)内部类 package testpack; public class Test1{ ...

  3. 0030 Java学习笔记-面向对象-垃圾回收、(强、软、弱、虚)引用

    垃圾回收特点 垃圾:程序运行过程中,会为对象.数组等分配内存,运行过程中或结束后,这些对象可能就没用了,没有变量再指向它们,这时候,它们就成了垃圾,等着垃圾回收程序的回收再利用 Java的垃圾回收机制 ...

  4. java学习(三)--- 修饰符

    访问修饰符: default.public.private.protected 非访问修饰符 static: 静态方法,静态变量 final: final变量: final变量能够显示的初始化并且只能 ...

  5. java学习(四)修饰符、运算符、循环结构、分支结构

    修饰符 一般是放在定义类,方法,变量的最前端 访问控制修饰符 修饰符 当前类 同一包内 子孙类 其他包 public Y Y Y Y protected Y Y Y N default Y Y N N ...

  6. java中static和final修饰符

    static和final修饰符 一.static修饰符 static表示“全局”或者“静态”的意思,用来修饰成员变量和成员方法,也可以形成静态static代码块,但是Java语言中没有全局变量的概念. ...

  7. 0022 Java学习笔记-面向对象-继承、多态、组合

    继承的特点 单继承:每个子类最多只有一个直接父类,注意是直接父类,间接父类个数不限 注意父类的概念:A-->B-->C-->D,在这里,ABC都是D的父类,C是D的直接父类,AB是D ...

  8. final修饰符—不可变

    final 修饰符 修饰类 不可以有子类 修饰变量 变量一旦获得初始值就不可改变,不能被重新赋值 成员变量:初始值必须有程序员显式设置,系统不会对其隐式初始化 类变量:静态初始化块 | 声明该类变量时 ...

  9. 程序设计基础·Java学习笔记·面向对象(下)

    Java程序设计基础之面向对象(下) (补充了上的一些遗漏的知识,同时加入了自己的笔记的ヾ(•ω•`)o) (至于为什么分P,啊大概是为了自己查笔记方便(?)应该是("` 3′") ...

随机推荐

  1. GeoServer中利用SLD配图之矢量图层配图

    文章版权由作者李晓晖和博客园共有,若转载请于明显处标明出处:http://www.cnblogs.com/naaoveGIS/. 1 背景 我们在ArcMap中可以直接通过symbol功能对图层进行定 ...

  2. PHP连接SQL Server相关配置及说明

     关于php怎么连接数据库,大家肯定有不同的看法,有的用wamp,有的用xamp,他们都是集成环境,方便操作.但有时,数据库的类型不同,要连接不同的数据库.今天和大家分享一下如何连接sql2000,服 ...

  3. 代码的坏味道(4)——过长参数列(Long Parameter List)

    坏味道--过长参数列(Long Parameter List) 特征 一个函数有超过3.4个入参. 问题原因 过长参数列可能是将多个算法并到一个函数中时发生的.函数中的入参可以用来控制最终选用哪个算法 ...

  4. 百度地图、ECharts整合HT for Web网络拓扑图应用

    前一篇谈及到了ECharts整合HT for Web的网络拓扑图应用,后来在ECharts的Demo中看到了有关空气质量的相关报表应用,就想将百度地图.ECharts和HT for Web三者结合起来 ...

  5. scikit-learn一般实例之一:保序回归(Isotonic Regression)

    对生成的数据进行保序回归的一个实例.保序回归能在训练数据上发现一个非递减逼近函数的同时最小化均方误差.这样的模型的好处是,它不用假设任何形式的目标函数,(如线性).为了比较,这里用一个线性回归作为参照 ...

  6. Rafy 领域实体框架演示(3) - 快速使用 C/S 架构部署

    本系列演示如何使用 Rafy 领域实体框架快速转换一个传统的三层应用程序,并展示转换完成后,Rafy 带来的新功能. <福利到!Rafy(原OEA)领域实体框架 2.22.2067 发布!> ...

  7. Xamarin.Android之引导页的简单制作

    0x01 前言 对于现在大部分的APP,第一次打开刚安装或更新安装的APP都会有几个引导界面,通常这几个引导页是告诉用户 APP有些什么功能或者修改了什么bug.新增了什么功能等等等. 下面就用Xam ...

  8. 【Win10开发】Toast通知——后台激活

    前篇文章我们写了关于Toast的前台激活,那么接下来就讲一讲它的后台激活.当通知出现时并不会出现app的界面,但是app能在后台获取到通知中的信息. 关于xaml和Toast通知架构我们在这里就不再赘 ...

  9. C#中实现excel文件批量导入access数据表中

    一 .界面简单设计如下: 二 .代码如下: using System; using System.Collections.Generic; using System.ComponentModel; u ...

  10. 转载:《TypeScript 中文入门教程》 12、类型推导

    版权 文章转载自:https://github.com/zhongsp 建议您直接跳转到上面的网址查看最新版本. 介绍 这节介绍TypeScript里的类型推论.即,类型是在哪里如何被推断的. 基础 ...