`

【面试】Java不可变类(基于final类的实现,final类只是不能被继承的类而已)

阅读更多
jdk的可变类和不可变类

所谓不可变类,是指当创建了这个类的实例后,就不允许修改它的属性值。在JDK的基本类库中,所有基本类型的包装类,如Integer和Long类,都是不可变类,java.lang.String也是不可变类,虽然他不是基本类型。

基本类型变量: boolean,byte, char, double ,float, integer, long, short
jdk的不可变类:jdk的java.lang包中 Boolean, Byte, Character, Double, Float, Integer, Long, Short, String.
jdk可变类举例:StringBuffer 可变类,java.util.Date 可变类

1.  可变类和不可变类(Mutable and Immutable Objects)的初步定义:
      可变类:当你获得这个类的一个实例引用时,你可以改变这个实例的内容。
      不可变类:当你获得这个类的一个实例引用时,你不可以改变这个实例的内容。不可变类的实例一但创建,其内在成员变量的值就不能被修改。

  
2. 如何创建一个自己的不可变类:
      .所有成员都是private
      .不提供对成员的改变方法,例如:setXXXX
      .确保所有的方法不会被重载。手段有两种:使用final Class(强不可变类),或者将所有类方法加上final(弱不可变类)。
      .如果某一个类成员不是原始变量(primitive)或者不可变类,必须通过在成员初始化(in)或者get方法(out)时通过深度clone方法,来确保类的不可变。

   3. 一个示例

import java.util.Date;                                         
public final class BrokenPerson                               
{                                                             
  private String firstName;                                   
  private String lastName;                                    
  private Date dob;                                           
                                        
  public BrokenPerson( String firstName,                   public BetterPerson( String firstName,
    String lastName, Date dob)                                             String lastName, Date dob)         
  {                                                        {                                                   
   this.firstName = firstName;                                this.firstName = firstName;        
   this.lastName = lastName;                                  this.lastName = lastName;          
   this.dob = dob; //error                                    this.dob = new Date( dob.getTime() ); //correct
}                                                          }                                                                 
                                                            
  public String getFirstName()
  {
   return this.firstName;
  }
  public String getLastName()
  {
   return this.lastName;
  }
  public Date getDOB()                                       public Date getDOB()                    
  {                                                          {                        
   return this.dob;    //error                                 return new Date( this.dob.getTime() );//correct
  }                                                          }
                                                                                                                          
}



如:String var = "abcd";Long i = 0L;
我们知道,当声明一个变量的时候,变量名实际上是该对象的指针;而我们在函数中传递该变量的时候实际上也是传递的指针.但基本类型传递的是他的实际值;


查看JAVA中的别名效应:
public class TestClass {
public int num;

public void doit(TestClass parm){
System.out.println("parm's num is "+parm.num);
//parm's num is 10
TestClass b = parm;
b.num = 11;
System.out.println("b's num is "+parm.num);
//b's num is 11
System.out.println("new parm's num is "+parm.num);
//new parm's num is 11
}
public static void main(String[] args){
TestClass a = new TestClass();
a.num = 10;
a.doit(a);
}
}

此时,a.num 的值是 11.
为什么?因为我们将 a--指向TestClass类的一个实例的指针,传递给doit();然后在方法中我们声明另一个实例,然后将指针指向了 a .实际上a,b现在都指向了同一个内存区.当修改 b 的属性时,a也受了影响.这就是别名效应


但如果传递的是基本类型,就不会出现。因为基本类型是不可变类;当声明新类,或对类的值做出修改的时候它都会创建一个新的类,然后再进行赋值;如:
String var = "abc";
var = "bcd";
这个过程中实际上创建了两个String 对象;同样,这种现象也发生在Long,Integer等上。


因为String也是不可变类,所以就引出了一个问题:在需要拼接字符串时,我们有时候会通过大量的:
str += "";语句来实现。其实这样是非常影响效率的。因为每一个 += 语句都会创建一个新的对象,并且意味着要清空以前的对象。这时候有另外一个类:StringBuffer 解决了这一问题。对StringBuffer的操作都是在一个对象上的,不会创建新的。


在某些特别情况下, String 对象的字符串拼接其实是被 JVM 解释成了 StringBuffer 对象的拼接,所以这些时候 String 对象的速度并不会比 StringBuffer 对象慢,而特别是以下的字符串对象生成中, String 效率是远要比 StringBuffer 快的:
String S1 = “This is only a” + “ simple” + “ test”;
StringBuffer sb = new StringBuilder(“This is only a”).append(“ simple”).append(“ test”);


你会很惊讶的发现,生成 String S1 对象的速度简直太快了,而这个时候 StringBuffer 居然速度上根本一点都不占优势。其实这是 JVM 的一个把戏,在 JVM 眼里,这个 
String S1 = “This is only a” + “ simple” + “test”;
其实就是: String S1 = “This is only a simple test”; 所以当然不需要太多的时间了。但大家这里要注意的是,如果你的字符串是来自另外的 String 对象的话,速度就没那么快了,譬如:
String S2 = “This is only a”;
String S3 = “ simple”;
String S4 = “ test”;
String S1 = S2 +S3 + S4;
这时候 JVM 会规规矩矩的按照原来的方式去做 在大部分情况下 StringBuffer > String
分享到:
评论

相关推荐

    Java面试题,他会给你很好的Java面试准备,第一,谈谈final, finally, finalize的区别,第二,Anonymous Inner Class (匿名内部类) 是否可以extends(继承)其它类,是否可以implements(实现)interface(接口)?

    被声明为final的变量必须在声明时给定初值,而在以后的引用中只能读取,不可修改。被声明为final的方法也同样只能使用,不能重载。 finally是一个异常处理时提供的finally块来执行任何清除操作。如果抛出一个异常,...

    Java常见笔试、面试题目深度剖析 final详解

    - `final`修饰的类不能被继承,防止了类层次结构的扩展。这样的类通常具有完备的设计,不希望其他开发者对其进行扩展。 - Java中的`String`类就是一个典型的`final`类,确保了字符串的不可变性。 4. **匿名内部类...

    java面试题 谈谈final, finally, finalize的区别

    - **不可变变量**:当一个变量被声明为`final`时,它的值一旦被赋值后就不能再改变。例如: ```java final int x = 10; // x 的值不能改变 ``` - **不可重写的方法**:如果一个方法被声明为`final`,那么子类...

    2022最新java面试题大全

    5. final 在 Java 中的作用:final 修饰的类不能被继承,final 修饰的方法可以被继承但不能被重写,final 修饰的变量必须初始化,地址值不能被改变,但对象的内容可以改变。 字符串操作 1. String 的不可变性:...

    java面试java面试java面试java面试java面试

    - String对象的`value`数组被final修饰,确保不可变性,优化性能和安全性,如哈希表操作。 不可变性的优点: - 安全性:字符串常用于安全敏感的操作,不可变性防止了意外修改。 - 性能:缓存哈希值,减少计算。 - ...

    JAVA面试题JAVA面试题JAVA面试题

    - `final`:用于修饰类、方法和变量,表示不可变或不可继承。 - `finally`:在异常处理中使用,无论是否发生异常,都会执行的代码块,确保资源的释放。 - `finalize`:对象被垃圾回收前调用的方法,通常用来进行...

    大公司的Java面试题集

     final 用于声明属性,方法和类,分别表示属性不可变,方法不可覆盖,类不可继承。 finally是异常处理语句结构的一部分,表示总是执行。 finalize是Object类的一个方法,在垃圾收集器执行的时候会调用被回收对象的...

    java面试资料汇总

    接口可以继承接口,抽象类可以实现接口,但抽象类不能继承实现类。 List、Set和Map不是Collection的子接口,而是继承自Collection接口的子接口。 数据连接池是一种数据库连接管理机制,用于高效地管理数据库连接。...

    java面试题,java框架面试题

    接口是抽象类的变体,通过接口可以实现多继承,接口中所有的方法都是抽象的(没有方法体),接口只能定义 static final 类型的成员变量,继承了接口的类必须实现接口中所有的方法。 二、 Java 集合框架 1. 集合...

    JAVA达内面试题JAVA达内面试题.doc

    7. Math和String不可继承:在Java中,Math和String是final类,不能被继承。instanceof后面跟的应该是OBJECT。构造器可以是私有的。 8. main方法签名:在Java中,main方法的签名可以是public static void main...

    Java面试题以及答案整理.pdf

    Final类不能被继承,其成员变量默认为final(不可变),final方法不能被重写。 11. **继承时类的执行顺序** 初始化顺序为:静态初始化块 -> 构造器 -> 实例初始化块。 12. **内部类的实现方式** 内部类分为:...

    疯狂Java面试题(疯狂Java讲义精粹附赠).pdf

    String 也是一个不可变类(它所包含的字符序列是不可改变的),因此如果程序需要使用的字符串所包含的字符序列需要经常改变,建议使用 StringBuffer(线程安全、性能略差)类或 StringBuilder 类。 5. int 和 ...

    华为JAVA面试题

    - final:表明某个类、方法或变量不能被继承、修改或重写。 - synchronized:用于同步线程,保证同一时间只有一个线程可以执行该方法或代码块。 - static:表示静态的,与类相关联,不需要实例就可以使用。 - super...

    Java面试宝典2018版

    9. final关键字修饰变量时,变量本身不可变。 10. “==”比较的是引用是否相同,equals方法比较的是对象内容是否相同。 11. 静态变量属于类,实例变量属于对象。 12. static方法中不能直接调用非static方法,因为非...

    Java岗面试核心MCA版

    - 抽象类不能用`final`修饰,因为`final`表示类不能被继承,这与抽象类的设计意图相悖。 2. **对象实例与对象引用**: - `new`关键字用于创建对象实例,这些实例存储在堆内存中。 - 对象引用是存储在栈内存中的...

    Java面试宝典 Java面试宝典

    Java面试宝典是Java开发者面试的必备知识点总结,涵盖了Java基础知识、Java面向对象编程、Java多态、Java继承、Java接口、Java抽象类、Java内部类、Java静态变量、Java构造器、Java重载和覆盖等方面。 Java基础知识...

    corejava_面试题

    Java基础知识讲解与面试题分析: 1. Java源文件中是否可以...Java中可以通过继承Thread类、实现Runnable接口以及使用Callable接口和FutureTask类来实现多线程。同步可以通过synchronized关键字或者显式锁Lock类实现。

    BATjava面试含答案

    8. 可以自定义 java.lang.String 类并编译成功,但不能被加载使用,具体请学习类加载机制。 本文总结了 BAT 面试中常见的 Java 面试题,涵盖了 List、Map、String 等基本数据结构和面试题,旨在帮助读者更好地了解 ...

    史上最全Java面试题目大集合

    java.lang.String类是final类型的,因此不可以继承这个类、不能修改这个类。为了提高效率节省空间,我们应该用StringBuffer类 3、int 和 Integer 有什么区别 Java 提供两种不同的类型:引用类型和原始类型(或内置...

Global site tag (gtag.js) - Google Analytics