static
-
static变量:
①只依赖于类存在,可以直接用类名直接引用,不依赖于对象实例存在,无需new对象来引用
②不管new多少个对象,static变量的值都共同存储在一个共同的空间(栈)。如果改变static变量的值,再次new新的对象时,相应的static值也是改变后的值
③static变量的生存期是从程序开始到结束一直存在。public class Potato{ static int price = 5;//静态变量 String content = ""; public Potato(int price, String content){ this.price = price; this.content = content; } //main函数 public static void main(String[] args) { System.out.println(Potato.price);//静态变量可以用类名直接引用,无需new对象。这里不可以Potato.content,content不是静态变量 Potato obj1 = new Potato(10,"青椒肉丝")//这里改变price的值 Potato obj2 = new Potato; System.out.println(Potato.price);//10 System.out.println(obj1.price);//10 System.out.println(obj2.price);//10 //这三个在内存里是同一个东西 } }
-
static方法:
①也直接可以用类名直接引用,无需new对象来引用
②静态方法中,只能使用静态变量
③静态方法禁止引用非静态方法public class StaticMethodTest{ int a = 111111; static int b = 222222;//静态变量 //静态方法 public static void hello { System.out.println("000000"); System.out.println(b);//right System.out.println(a);//error 静态方法中不能使用非静态变量 hi();//error } //非静态方法 public static void hi { System.out.println("333333"); hello();//ok } }
-
static块:
①static块只在第一次被加载的时候运行
②执行顺序:static块 > 匿名块 > 构造函数
static块:static{ ... }
匿名块:
{ ... }
单例模式
单例模式:内存空间中,一个类有且只有一个对象存在。
单例模式属于设计模式中经典的一种,属于创建型模式类型
设计模式: 在软件开发过程中,经过验证的,用于解决在特定环境下的、重复出现的、特定问题的解决方案。设计模式包括:创建型、结构型和行为型
单例模式特点:
- 采用static来共享对象实例
- 采用private构造函数,防止外界new操作
eg.
public class Singleton{
//属性全部私有化,只能用方法来获取属性值
private static Singleton obj = new Singleton();//只在这里new一次,obj是静态变量,会一直存在
private String content;
//构造函数私有化,确保只能在类的内部调用构造函数,外界无法new(new操作就是调用构造函数)
private Singleten(){
this.content = "abc";
}
//只能用方法来获取属性值
public String getContent(){
return content;
}
public void setContent(String content){
this.content = content;
}
//用静态方法来获取对象
public static Singleton getInstance(){
return obj;//返回静态Singleton对象
//不关在外界调用多少次getInstance(),所获取的对象都是这唯一的一个obj
}
//main函数
public static void main(String[] args){
Singleton obj1 = Singleton.getInstance();//获取对象
}
}
获取对象方法:
Singleton a = Singleton.getInstance();
由此达到了单例模式的要求(只new一次)
final
-
final可以用来修饰:类、方法、字段
//修饰类 final public class FinalFather{...} //修饰方法 public final void f1(){...} //修饰字段 final int a;
-
final的类,不能被继承,没有子类。
-
父类如果有final的方法,子类中不能重写此方法
-
final修饰的变量,不能被再次赋值(有点类似于c的const)
-
final修饰的对象,不能修改其指针(但是可以修改对象内部的值)
一个类:public class FinalObject{ int a = 10; }
main:
public class FinalObjectTest{ public static void main(){ final Finalobject obj1 = new Finalobject();//final修饰对象,意思是final对象的指针固定了,obj1只能指向这一个对象,不可改变 //可以改变obj1.a的值 obj1.a = 20;//right //不可以将新对象赋给obj1 obj1 = new FinalObject();//error } }
常量设计
Java中没有const关键字
不能修改:final;不会修改/只读/只要一份:static;方便访问:public
- 因此:public static final + 变量类型 + 变量名
- Java常量的变量命名规则:全大写,以连字符相连
- 接口内定义的变量编译器会默认是常量,如果修改其值会报错
常量池
常量池:就是一块特殊的内存,保存在编译期间就已经确定的数据
特点:相同的值只储存一份,节省内存,共享访问(Java将有常量池的常量的每个值(有限)都保存在一个固定的内存地址里)
基本类型的包装类的常量池:
- Boolean:true, false
- Byte: -128~127
- Character: 0~127(ASCLL码的基础部分)
- Short,Int,Long:-128~127
- Float, Double: 没有缓存(常量池),因为小数太多了
eg.//这样创建的对象b1和b2指向了同一块内存 Boolean b1 = true; Boolean b2 = true; System.out.println(String.valueOf(b1 == b2))//true //注意,这里的==是判断指针值是否相同(因为b1和b2是对象,可以看成一个指针),即判断是否指向同一个地址,而不是判断该地址的值是否相同 Float f1 = 0.5f;//Float没有常量池,f1和f2指向的内存不同 Float f2 = 0.5f; System.out.println(String.valueOf(f1 == f2))//false ,没有常量池,不指向同一个地址
常量字符串也有常量池缓存机制
这里就有一个基本类型的包装类和字符串的创建问题了:
1. 常量式(字面量)赋值创建,放在栈内存(将被常量化),栈内存读取速度块但容量小
//这样直接赋值就会被常量化
Integer a = 10;
String b = "abc"
2.new对象进行创建,放在堆内存(一个新的地址中,不会常量化),堆内存读取速度慢但容量大
//这样不会常量化
Integer c = new Integer(10);
String d = new String("abc");
因为new会根据构造函数来创建对象;而直接赋值会有一个基本变量自动装箱变成对象的过程,此时产生的对象就会指向变量池中该值的固定内存
eg.基本类型的包装类
public class BoxClassTest{
public static void main(String[] args){
int i1 = 10;
Integer i2 = 10;//自动装箱操作,将基本变量变成常量对象,使i2指向10
System.out.println(i1 == i2)//true
//自动拆箱,基本类型和包装类型进行比较,包装类型自动拆箱
Integer i3 = new Integer();//这里直接new一个对象
System.out.println(i1 == i3)//true
//自动拆箱,基本类型和包装类型进行比较,包装类型自动拆箱
System.out.println(i2 == i3)//false
//两个对象比较,比较其地址,一个在堆内存,一个在栈内存,地址肯定不同了三
Integer i4 = i2 + i3//i4是常量对象,因为对象加法也会自动拆箱
}
}
eg.字符串
public class StringNewTest{
public static void main(String[] args){
String s0 = "abcdef";
String s1 = "abc";
String s3 = s1 + "def";//涉及到变量,编译器不优化
String s4 = "abc" + "def"//都是常量,编译器自动优化成abcdef
System.out.println(s3 == s4)//false
System.out.println(s0 == s3)//true
}
}
不可变对象
-
不可变对象:一旦创建,不能更改的对象(的状态/值)
-
不可变对象有:八个基本类型的包装类,String,BigInteger,BigDecimal等
-
不可变对象也是对象,所以函数还是传指针,但由于不可变,形参变量在函数体内修改后指向新内存,外部的实参的指针不改动
eg.public static void change(String b)//这里的b也指向abc { b = "def";//这里的b指向abc的指针断掉,重新指向def } String a = new String("abc");//这里a指针指向abc change(a);//a仍然指向abc,没变 System.out.println(a);//abc
-
如何创建不可变对象
①使用final(使值固定)和private(不可访问),创建的对象是不可变的
②不提供setter方法,没有setter方法,外部就不可修改
③将类设成final,或所有的方法都是final
Java字符串
- 字符串是典型的不可变对象,那么是怎样的不可修改呢:
eg.
例子说明这里的abc是不可修改的,abcdef在一个新的内存里String a ="abc";//这里的a指向abc a = a + "def"//内存里的abc仍然在,只是a的指针断掉重新指向abcdef这一块新的内存地址,abc的这块内存就浪费了
- String 定义:
String a = "abc";//常量赋值,栈内存
String b = new String("abc");//new对象,堆内存
- 字符串内容比较:equals方法
- 判断是否指向同一个对象:指针比较==
- StringBuffer和StringBuilder是可变对象,我们可以使用他们的很多方法处理字符串
①append(String str)
//在末尾添加的字符串
②delete(int start, int end)
//删除开始、结束位置
③insert(int offset, String str)
//插入位置,插入字符串
④replace(int start, int end, String str)
⑤reverse()
//翻转字符串
String,StringBuffer和StringBuilder的效率区别:
import java.util.Calendar;//导入时间包
public class StringAppendTest {
public static void main(String[] args) {
int n = 50000;
Calendar t1 = Calendar.getInstance();//获取系统时间
String a = new String();
for (int i=0;i<n;i++)
{
a=a+i+",";
}
System.out.println(Calendar.getInstance().getTimeInMillis() - t1.getTimeInMillis());//输出所需时间 2399ms
Calendar t2 = Calendar.getInstance();
StringBuffer b = new StringBuffer("");
for(int i=0;i<n;i++)
{
b.append(i);
b.append(",");
}
System.out.println(Calendar.getInstance().getTimeInMillis() - t2.getTimeInMillis());//12ms
Calendar t3 = Calendar.getInstance();
StringBuilder c = new StringBuilder();
for(int i=0;i<n;i++)
{
b.append(i);
b.append(",");
}
System.out.println(Calendar.getInstance().getTimeInMillis() - t3.getTimeInMillis());//8ms
}
}
可变对象和不可变对象传递参数的区别:
public class ArgumentPassing {
public static void changeValue(int a)
{
a=10;
}
public static void changeValue(String s1)
{
s1 = "def";
}
public static void changeValue(StringBuffer s1)
{
s1.replace(0,3,"def");
}
public static void main(String[] args) {
int a = 5;
String b = "abc";
StringBuffer c = new StringBuffer("abc");
changeValue(a);
changeValue(b);//String类型的b是不可变对象,虽然传的是指针,但是当形参修改时,并不会修改形参指向的数据,而是会重新创建一个空间储存改变的值,形参的指针重再指向它,原值不改,那么很显然,实参指向这个原来的值也不会改变
changeValue(c);//StringBuffer类型的c是可变对象,形参和实参指向同一块内存,当形参改变是,可以直接改变形参指向的空间的变量
System.out.println(a);//5
System.out.println(b);//abc
System.out.println(c);//def
}
}