[Java核心技术] static、final和常量设计
![[Java核心技术] static、final和常量设计 /java04/featured-image.png](/java04/featured-image.png)
目录
Java 核心技术读书笔记——Java static、final和常量设计
1 static
static 静态的,Java中特殊的关键字,可作用在变量、方法、类、匿名方法块
1.2 静态变量
- 静态变量,类共有成员
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39public class Potato { static int price = 5; String content = ""; public Potato(int price, String content) { this.price = price; this.content = content; } public static void main(String[] a) { System.out.println(Potato.price); //Potato.content wrong System.out.println("----------------------------------"); Potato obj1 = new Potato(10,"青椒土豆丝"); // 以下两行都输出10,Potato.price 和 obj1.price 在内存中是同一个东西 System.out.println(Potato.price); System.out.println(obj1.price); System.out.println("----------------------------------"); Potato obj2 = new Potato(20,"酸辣土豆丝"); System.out.println(Potato.price); System.out.println(obj2.price); } } /** * * 输出结果: * 5 * ---------------------------------- * 10 * 10 * ---------------------------------- * 20 * 20 * */ static变量只依赖于类存在(通过类访问即可),不依赖于对象实例存在。即可以用过Potato.price即可访问- 所有的对象实例,如上例中的
obj1和obj2关于price变量的值共享存储在一个共同的空间(栈)
1.2 静态方法
- 静态方法也无需通过对象来引用,而通过类名可以直接引用。
- 静态方法中,只能使用静态变量,不能使用非静态变量。
- 静态方法不能调用非静态方法。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28public class StaticMethodTest { int a = 111111; static int b = 222222; public static void hello() { System.out.println("000000"); System.out.println(b); //System.out.println(a); //error, cannot call non-static variables //hi() //error, cannot call non-static method } public void hi() { System.out.println("333333"); hello(); //ok, call static methods System.out.println(a); //ok, call non-static variables System.out.println(b); //ok, call static variables } public static void main(String[] a) { StaticMethodTest.hello(); //StaticMethodTest.hi(); //error, 不能使用类名来引用非静态方法 StaticMethodTest foo = new StaticMethodTest(); foo.hello(); //warning, but it is ok foo.hi(); //right } }
1.3 static修饰类(内部类)
使用机会较少
1.4 static块
static块只在类第一次被加载时调用,即程序运行期间,这段代码只运行一次- 执行顺序:
static块>匿名块>构造函数 - 不建议编写块代码,块代码会给程序带来混淆。建议将块代码封装成函数再调用。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50// StaticBlock.java class StaticBlock { //staticl block > anonymous block > constructor function // 静态代码块 static { System.out.println("22222222222222222222"); } // 匿名代码块 { System.out.println("11111111111111111111"); } // 构造函数 public StaticBlock() { System.out.println("33333333333333333333"); } // 匿名代码块 { System.out.println("44444444444444444444"); } } //StaticBlockTest.java public class StaticBlockTest { public static void main(String[] args) { System.out.println("000000000000000"); // TODO Auto-generated method stub StaticBlock obj1 = new StaticBlock(); StaticBlock obj2 = new StaticBlock(); } } /** * * 运行结果: * 000000000000000 * 22222222222222222222 * 11111111111111111111 * 44444444444444444444 * 33333333333333333333 * 11111111111111111111 * 44444444444444444444 * 33333333333333333333 * */
2 单例模式(Singleton)
-
单例模式(单态模式,Singleton),保证一个类有且只有一个对象
- 采用
static来共享对象实例 - 采用
private构造函数,防止外界new操作 - 限定某一个类在整个程序运行过程中,只能保留一个实例对象在内存空间
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37public class Singleton { private static Singleton obj = new Singleton(); //共享同一个对象 private String content; private Singleton() //确保只能在类内部调用构造函数 { this.content = "abc"; } public String getContent() { return content; } public void setContent(String content) { this.content = content; } public static Singleton getInstance() { //静态方法使用静态变量 //另外可以使用方法内的临时变量,但是不能引用非静态的成员变量 return obj; } public static void main(String[] args) { Singleton obj1 = Singleton.getInstance(); System.out.println(obj1.getContent()); // abc Singleton obj2 = Singleton.getInstance(); System.out.println(obj2.getContent()); // abc obj2.setContent("def"); System.out.println(obj1.getContent()); System.out.println(obj2.getContent()); System.out.println(obj1 == obj2); // true, obj1和obj2指向同一个对象 } } - 采用
-
单例模式是
GoF的23种设计模式(Design Pattern)中经典的一种,属于创造型模型类型。
笔记
设计模式:在软件开发过程中,经过验证的,用于解决在特定环境下的、重复出现的、特定问题的结局方案。
3 final
-
Java的final关键字可以用来修饰类、方法、字段 -
final的类,不能被继承1 2 3 4 5 6 7final public class FinalFather { } class Son1 extends FinalFather { } -
父类中如果有
final的方法,子类中不能改写此方法1 2 3 4 5 6 7 8 9 10 11 12 13 14 15// FinalMethodFather.java public class FinalMethodFather { public final void f1() { } } //FinalMethodSon.java public class FinalMethodSon extends FinalMethodFather{ public void f1() // error Cannot override the final method from FinalMethodFather { } } -
final的变量,不能再次赋值- 如果是基本型的变量,不能修改其值
1 2 3 4 5 6 7 8public class FinalPrimitiveType { public static void main(String[] args) { // TODO Auto-generated method stub final int a = 5; a=10; // error The final local vaiable a cannot be assigned. It must be blank and not using a compound assignment } }- 如果是实例对象,那么不能修改其指针(但是可以修改对象内的值)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18class FinalObject { int a = 10; } public class FinalObjectTest { public static void main(String[] args) { final FinalObject obj1 = new FinalObject(); System.out.println(obj1.a); obj1.a = 20; System.out.println(obj1.a); obj1 = new FinalObject(); //final对象不能变更指针 } }
4 常量设计和常量池
4.1 常量
- 常量:一种不会修改的变量
- 不能修改,
final - 不会修改/只读/只要一份,
static - 方便访问
public final和static可以颠倒顺序
- 不能修改,
- 常量的定义
1 2public static final String = DEFAULT_COUNTRY = "China"; //建议变量名字全大写,以字符相连,如 DEFAULT_COUNTRY - 一种特殊的常量:接口内定义的变量默认为常量
4.2 常量池
-
Java为很多基本类型的包装类/字符串都建立常量池
-
常量池:相同的值只存储一份,节省内存,共享访问
-
基本类型的包装类
Boolean:true,falseByte,Character:\u00000 —— \u007f(0 —— 127)Short,Int,Long:-128 ~ 127Float,Double: 没有缓存(常量池)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35public class CacheTest { public static void main(String[] args) { Boolean b1 = true; //true,false Boolean b2 = true; System.out.println("Boolean Test: " + String.valueOf(b1 == b2)); //true Byte b3 = 127; //\u0000-\u007f Byte b4 = 127; System.out.println("Byte Test: " + String.valueOf(b3 == b4)); //true Character c1 = 127; //\u0000-\u007f Character c2 = 127; System.out.println("Character Test: " + String.valueOf(c1 == c2)); //true Short s1 = -128; //-128~127 Short s2 = -128; System.out.println("Short Test: " + String.valueOf(s1 == s2)); //true Integer i1 = -128; //-128~127 Integer i2 = -128; System.out.println("Integer Test: " + String.valueOf(i1 == i2)); //true Long l1 = -128L; //-128~127 Long l2 = -128L; System.out.println("Long Test: " + String.valueOf(l1 == l2)); //true Float f1 = 0.5f; Float f2 = 0.5f; System.out.println("Float Test: " + String.valueOf(f1 == f2)); //false Double d1 = 0.5; Double d2 = 0.5; System.out.println("Double Test: " + String.valueOf(d1 == d2)); //false } } -
Java 为常量字符串都建立常量池缓存机制
-
字符串常量
1 2 3 4 5 6 7 8 9 10 11public class StringConstantTest { public static void main(String[] args) { String s1 = "abc"; String s2 = "abc"; String s3 = "ab" + "c"; //都是常量,编译器将优化,下同 String s4 = "a" + "b" + "c"; System.out.println(s1 == s2); //true System.out.println(s1 == s3); //true System.out.println(s1 == s4); //true } } -
基本类型的包装类和字符串有两种创建方式
- 常量式(字面量)赋值创建,放在栈内存(将被常量化)
1 2Integer a = 10; Strign b = "abc"; new对象进行创建,放在堆内存(不会常量化)1 2Integer c = new Integer(10); String d = new String("abc");- 这两种创建方式导致创建的对象存放的位置不同。
- 栈内存读取速度快但容量小,堆内存读取速度慢但容量大。
- 查看
BoxClassTest.java分析Integer类1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37/** * * 基本类型和包装类比较,将对包装类自动拆箱 * 对象比较,比较地址 * 加法(+)会自动拆箱 * */ public class BoxClassTest { public static void main(String[] args) { int i1 = 10; Integer i2 = 10; // 自动装箱 System.out.println(i1 == i2); //true // 自动拆箱 基本类型和包装类进行比较,包装类自动拆箱 Integer i3 = new Integer(10); System.out.println(i1 == i3); //true // 自动拆箱 基本类型和包装类进行比较,包装类自动拆箱 System.out.println(i2 == i3); //false // 两个对象比较,比较其地址。 // i2是常量,放在栈内存常量池中,i3是new出对象,放在堆内存中 Integer i4 = new Integer(5); Integer i5 = new Integer(5); System.out.println(i1 == (i4+i5)); //true System.out.println(i2 == (i4+i5)); //true System.out.println(i3 == (i4+i5)); //true // i4+i5 操作将会使得i4,i5自动拆箱为基本类型并运算得到10. // 基础类型10和对象比较, 将会使对象自动拆箱,做基本类型比较 Integer i6 = i4 + i5; // +操作使得i4,i5自动拆箱,得到10,因此i6 == i2. System.out.println(i1 == i6); //true System.out.println(i2 == i6); //true System.out.println(i3 == i6); //false } } - 查看
StringNewTest.java分析String类1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36/** * * 常量赋值(堆内存)和 new 创建(栈内存)不是同一个对象 * 编译器只会优化确定的字符串,并缓存 * */ public class StringNewTest { public static void main(String[] args) { String s0 = "abcdef"; String s1 = "abc"; String s2 = "abc"; String s3 = new String("abc"); String s4 = new String("abc"); System.out.println(s1 == s2); //true 常量池 System.out.println(s1 == s3); //false 一个栈内存,一个堆内存 System.out.println(s3 == s4); //false 两个都是堆内存 System.out.println("========================="); String s5 = s1 + "def"; //涉及到变量,故编译器不优化 String s6 = "abc" + "def"; //都是常量 编译器会自动优化成abcdef String s7 = "abc" + new String ("def");//涉及到new对象,编译器不优化 System.out.println(s5 == s6); //false System.out.println(s5 == s7); //false System.out.println(s6 == s7); //false System.out.println(s0 == s6); //true System.out.println("========================="); String s8 = s3 + "def";//涉及到new对象,编译器不优化 String s9 = s4 + "def";//涉及到new对象,编译器不优化 String s10 = s3 + new String("def");//涉及到new对象,编译器不优化 System.out.println(s8 == s9); //false System.out.println(s8 == s10); //false System.out.println(s9 == s10); //false } }
- 常量式(字面量)赋值创建,放在栈内存(将被常量化)
5 不可变对象和字符串
5.1 不可变对象(Immutable Object)
- 一旦创建,这个对象(状态/值)不能被更改
- 其内在的成员变量的值就不能再次修改
- 八个基本类型的包装类
String,BigInteger和BigDecimal等- 不可变对象也是传指针(引用)
- 由于不可变,临时变量指向新内存,外部实参指针不改动
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15//示例一: String a = new String("abc"); // a -> abc String b = a; // b -> abc System.out.println(b); a = "def"; // a -> def b -> abc System.out.println(b); // abc //示例二: public static void change(String b) { b = "def"; } a = new String("abc"); // a -> abc change(a); // a -> abc,b -> abc 函数体内部 b -> def System.out.println(a); //abc
5.1.1 如何创建不可变对象
immutable对象是不可改变的,有改变,需clone/new一个对象进行修改- 所有的属性都是
final和private的 - 不提供
setter方法 - 类是
final的,或者所有的方法都是final的 - 类中包含
mutable对象,那么返回拷贝需要深度clone
5.1.2 优点
- 只读,线程安全
- 并发读,提高性能
- 可以重复使用
5.1.3 缺点
- 制造垃圾,浪费空间
- 对不可变对象进行修改时,会新开辟空间,旧对象则被搁置,知道垃圾回收
5.2 字符串
- 字符串是Java使用最多的类,是一种典型的不可变对象
- String定义
1 2String a = "abd"; //常量赋值,栈内存分配 String b = new String("abc"); //new对象,堆内存分配 - 字符串内容比较:
equals方法 - 是否指向同一个对象:指针比较
== - Java 常量池
- 保存在编译期间就已经确定的内存
- 是一块特殊的内存
- 相同的常量字符串只存储一份,节省内存,共享访问
5.2.1 字符串加法
|
|
- 使用StringBuffer/StringBuilder类的append方法进行修改
- StringBuffer/StringBuilder的对象都是可变对象
- StringBuffer 同步,线程安全,修改快速
- StringBuilder 不同步,线程不安全,修改更快
|
|