Java核心技术系列之华东师范大学Java核心技术第六章static、final和常量设计
static
通俗地来说,static定义的东西共享同一个内存。
对于static
的具体作用方面,举个例子,
static作用在变量上
package chapter6.staticExample;
public 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 不能直接输出
System.out.println("-------------");
Potato obj1 = new Potato(10, "青椒土豆丝");
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
这两行代码
System.out.println(Potato.price);
System.out.println(obj1.price);
都输出10,Potato.price
和 obj1.price
在内存中是同一个东西,也就是内存中只有一个price
。同理,Potato.price
和 obj2.price
在内存中是同一个东西。因此,Potato.price
、obj1.price
和 obj2.price
在内存中是同一个东西,共享存储在同一个空间。即static
值只有一个拷贝:
static作用在方法上
总之,两句话:
1、静态方法里只能用静态变量,不能使用非静态变量
2、非静态方法中静态变量和非静态变量都可以使用
StaticMethodTest.java
代码如下:
package chapter6.staticExample;
public class StaticMethodTest {
int a = 1111;
static int b = 2222;
public static void hello()
{
System.out.println("00000");
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("33333333");
hello(); //ok, can call static method
System.out.println(a); //ok, can call non-static method
System.out.println(b); //ok, can call static variables
}
public static void main(String[] args) {
StaticMethodTest.hello();
// StaticMethodTest.hi(); //error, 不能使用类名来引用非静态方法
StaticMethodTest foo = new StaticMethodTest();
foo.hello(); //warning, but it is ok. 建议采用类名直接调用静态方法
foo.hi(); //right
}
}
static修饰代码块
StaticBlock.java
代码如下:
package chapter6.staticExample;
public class StaticBlock {
// static block > anonymous block > constructor function
// static block
static {
System.out.println("222222");
}
// anonymous block
{
System.out.println("111111");
}
// constructor function
public StaticBlock() {
System.out.println("333333");
}
// anonymous block
{
System.out.println("444444");
}
}
StaticBlockTest.java
代码如下:
package chapter6.staticExample;
public class StaticBlockTest {
public static void main(String[] args) {
System.out.println("000000");
StaticBlock obj1 = new StaticBlock();
StaticBlock obj2 = new StaticBlock();
}
}
执行结果如下:
000000
222222
111111
444444
333333
111111
444444
333333
首先呢,输出000000。
其次,在类加载时需要先调用static模块,此时输出222222。
接下来,调用的是匿名函数,因此,输出
111111
444444
再接下来,是构造函数。因此,此时输出333333。
第二次实例化时,由于static只是在类第一次被加载时调用,因此,按匿名函数、构造函数输出:
111111
444444
333333
比如封装成init()
或者initial()
等初始化函数,放在构造函数中调用等会更明晰一些。
总结
单例模式(创建型模式)
Singleton
代码如下:
package chapter6.SingletonExample;
public class Singleton {
private static Singleton singleton = 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() // 本质上获取的还是new出来的singleton
{
// 静态方法使用静态变量
// 另外,可以使用方法内的临时变量,但是不能引用非静态的成员变量
return singleton;
}
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 指向同一个内存
}
}
上述例子输出结果:
abc
abc
def
def
true
总结
final关键字
final的类不能被继承的例子,FinalFather.java
具体代码如下:
package chapter6.FinalExample;
final public class FinalFather {
}
class Son1 extends FinalFather // error
{
}
final的方法不能被改写,FinalFatherSon.java
具体代码如下:
总结
常量设计
常量定义及使用
如果将接口内的变量修改成private
私有变量,则编译通不过。
常量池
看个例子,第一个输出相等是true,第二个是false;
package chapter6.cache;
public class CacheTest {
public static void main(String[] args) {
boolean a1 = true;
boolean a2 = true;
System.out.println("boolean test is " + String.valueOf(a1 == a2)); //true
Byte b1 = 127; //-127~128
Byte b2 = 127;
System.out.println("Byte test is " + String.valueOf(b1 == b2)); //true
Character c1 = 127; //\u0000 - \u007f
Character c2 = 127;
System.out.println("Character test is " + String.valueOf(c1 == c2)); //true
Short s1 = 127; //-128~127
Short s2 = 127;
System.out.println("Short test is " + String.valueOf(s1 == s2)); //true
Integer i1 = 127; //-128~127
Integer i2 = 127;
System.out.println("Integer test is " + String.valueOf(i1 == i2)); //true
Long l1 = 127L; //-128~127
Long l2 = 127L;
System.out.println("Long test is " + String.valueOf(l1 == l2)); //true
Float f1 = 0.5f; //-128~127
Float f2 = 0.5f;
System.out.println("Long test is " + String.valueOf(f1 == f2)); //false, 说明内存中存在两个0.5
}
}
那如果数值范围超出范围的最大值/最小值呢?是否结果一样呢?我们做个实验:
package chapter6.cache;
public class CacheTest {
public static void main(String[] args) {
boolean a1 = true;
boolean a2 = true;
System.out.println("boolean test is " + String.valueOf(a1 == a2)); //true
Byte b1 = 127; //-127~128, 128为error
Byte b2 = 127;
System.out.println("Byte test is " + String.valueOf(b1 == b2)); //true
Character c1 = 12899; //\u0000 - \u007f
Character c2 = 12899;
System.out.println("Character test is " + String.valueOf(c1 == c2)); //false,超界
Short s1 = 128; //-128~127
Short s2 = 128;
System.out.println("Short test is " + String.valueOf(s1 == s2)); //false,超界
Integer i1 = 128; //-128~127
Integer i2 = 128;
System.out.println("Integer test is " + String.valueOf(i1 == i2)); //false,超界
Long l1 = 128L; //-128~127
Long l2 = 128L;
System.out.println("Long test is " + String.valueOf(l1 == l2)); //false,超界
Float f1 = 128f; //-128~127
Float f2 = 128f;
System.out.println("Long test is " + String.valueOf(f1 == f2)); //false, 说明内存中存在两个0.5
}
}
字符串常量池缓存机制
常量池作用
1、节约内存
2、共享访问
存储方式 | 优点 | 缺点 |
---|---|---|
栈内存 | 读取速度快 | 容量小 |
堆内存 | 读取速度慢 | 容量大 |
这两种创建方式进行举例比较两者是否相等:
- 基本类型
package chapter6.cache;
public class BoxClassTest {
public static void main(String[] args) {
int i1 = 10; // 基本类型
Integer i2 = 10; //包装类,自动装箱,10原本是小int,装箱变成i2。放在栈内存中。
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,是int类型,不是Integer对象
Integer i6 = i4+ i5;
System.out.println(i1 == i6); // true
System.out.println(i2 == i6); // true
System.out.println(i3 == i6); // false
}
}
- 字符串类型