Java入门(四)
equals()与==
==
比较的是两个值是否相等,这里的值分为两种情况:
基本类型的值,即float、double、byte、short、int、long、char、boolean这八种java的基本数据类型,这里比较的就是值本身,举个例子:
1 | int a=2; |
而对于非基本数据类型(或者说引用类型)的变量,比较的就不是“值”本身,因为引用类型的变量存储的并不是“值”本身,而是预期关联对象在内存中的地址,举个例子:
1 | String str = new String("hello"); |
equals()
equals方法是基类Object中的方法,因此对于所有的继承于Object的类都会有该方法。equals比较的是两个对象的引用是否相等,即是否指向同一个对象,不过看下下面的代码:
1 | public class Main { |
之所以会变成这样是因为Java的equals方法在用来比较String、Double,Date,Integer等对象时,其内部对equals方法进行了重写(可以直接去看自己JDK的中String.java的源代码,确实是对equals方法进行了重写),使其用来比较指向对象所处的内容是否相等。
小结
- 对于==,如果作用于基本数据类型的变量,则直接比较其存储的 “值”是否相等,如果作用于引用类型的变量,则比较的是所指向的对象的地址。
- 对于equals方法,注意:equals方法不能作用于基本数据类型的变量如果没有对equals方法进行重写,则比较的是引用类型的变量所指向的对象的地址,诸如String、Date等类对equals方法进行了重写的话,比较的是所指向的对象的内容。
本节内容转载于:浅谈Java中的equals和==
static、final和static final
final
- final类不能被继承,没有子类,final类中的方法默认是final的
- 在设计类时候,如果这个类不需要有子类,类的实现细节不允许改变,并且确信这个类不会再被扩展,那么就设计为final类
- final方法不能被子类的方法覆盖,但可以被继承
- 如果一个类不允许其子类覆盖某个方法,则可以把这个方法声明为final方法
- 把方法锁定,防止任何继承类修改它的意义和实现
- 高效。编译器在遇到调用final方法时候会转入内嵌机制,大大提高执行效率
- final成员变量表示常量,只能被赋值一次,赋值后值不再改变
- final修饰的变量有三种:静态变量、实例变量和局部变量,分别表示三种类型的常量
- final变量定义的时候,可以先声明,而不给初值,这中变量也称为final空白,无论什么情况,编译器都确保空白final在使用之前必须被初始化。但是,final空白在final关键字final的使用上提供了更大的灵活性,为此,一个类中的final数据成员就可以实现依对象而有所不同,却有保持其恒定不变的特征
- final不能用于修饰构造方法
- 父类的private成员方法是不能被子类方法覆盖的,因此private类型的方法默认是final类型的
- 当函数参数为final类型时,你可以读取使用该参数,但是无法改变该参数的值
例子
1 | //关于用final修饰方法的情况 |
1 | //关于final修饰成员变量的情况 |
1 | //关于final修饰函数参数的情况 |
static
- static表示“全局”或者“静态”的意思,用来修饰成员变量和成员方法
- 被static修饰的成员变量和成员方法独立于该类的任何对象。也就是说,它不依赖类特定的实例,被类的所有实例共享
- static对象可以在它的任何对象创建之前访问,无需引用任何对象
- 用public修饰的static成员变量和成员方法本质是全局变量和全局方法,当声明它类的对象时,不生成static变量的副本,而是类的所有实例共享同一个static变量
- static变量前可以有private修饰,表示这个变量可以在类的静态代码块中,或者类的其他静态成员方法中使用,但是不能在其他类中通过类名来直接引用。原因是private是访问权限限定,static表示不要实例化就可以使用
- static修饰的成员变量和成员方法习惯上称为静态变量和静态方法,可以直接通过类名来访问
- 用法为:
- 类名.静态方法名(参数列表…)
- 类名.静态变量名
- 用法为:
static变量
静态变量或类变量
- 被static修饰的变量
- 在内存中只有一个拷贝(节省内存),JVM只为静态分配一次内存,在加载类的过程中完成静态变量的内存分配,可用类名直接访问(方便),当然也可以通过对象来访问(但是这是不推荐的)
实例变量
- 没有被static修饰的变量
- 每创建一个实例,就会为实例变量分配一次内存,实例变量可以在内存中有多个拷贝,互不影响(灵活)
静态方法
- 静态方法可以直接通过类名调用,任何的实例也都可以调用,因此静态方法中不能用this和super关键字,不能直接访问所属类的实例变量和实例方法(就是不带static的成员变量和成员成员方法),只能访问所属类的静态成员变量和成员方法。因为实例成员与特定的对象关联!
- 因为static方法独立于任何实例,因此static方法必须被实现,而不能是抽象的abstract
static代码块
- 也叫静态代码块
- 是在类中独立于类成员的static语句块,可以有多个,位置可以随便放,它不在任何的方法体内,JVM加载类时会执行这些静态的代码块,如果static代码块有多个,JVM将按照它们在类中出现的先后顺序依次执行它们,每个代码块只会被执行一次
1 | //static代码块的例子 |
static final
- static final用来修饰成员变量和成员方法,可简单理解为“全局常量”
- 对于变量,表示一旦给值就不可修改,并且通过类名可以访问
- 对于方法,表示不可覆盖,并且可以通过类名直接访问
- 对于被static和final修饰过的实例常量,实例本身不能再改变了,但对于一些容器类型(比如,ArrayList、HashMap)的实例变量,不可以改变容器变量本身,但可以修改容器中存放的对象,这一点在编程中用到很多
例子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 TestStaticFinal {
private static final String strStaticFinalVar = "aaa";
private static String strStaticVar = null;
private final String strFinalVar = null;
private static final int intStaticFinalVar = 0;
private static final Integer integerStaticFinalVar = new Integer(8);
private static final ArrayList<String> alStaticFinalVar = new ArrayList<String>();
private void test() {
System.out.println("-------------值处理前----------\r\n");
System.out.println("strStaticFinalVar=" + strStaticFinalVar + "\r\n");
System.out.println("strStaticVar=" + strStaticVar + "\r\n");
System.out.println("strFinalVar=" + strFinalVar + "\r\n");
System.out.println("intStaticFinalVar=" + intStaticFinalVar + "\r\n");
System.out.println("integerStaticFinalVar=" + integerStaticFinalVar + "\r\n");
System.out.println("alStaticFinalVar=" + alStaticFinalVar + "\r\n");
//strStaticFinalVar="哈哈哈哈"; //错误,final表示终态,不可以改变变量本身.
strStaticVar = "哈哈哈哈"; //正确,static表示类变量,值可以改变.
//strFinalVar="呵呵呵呵"; //错误, final表示终态,在定义的时候就要初值(哪怕给个null),一旦给定后就不可再更改。
//intStaticFinalVar=2; //错误, final表示终态,在定义的时候就要初值(哪怕给个null),一旦给定后就不可再更改。
//integerStaticFinalVar=new Integer(8); //错误, final表示终态,在定义的时候就要初值(哪怕给个null),一旦给定后就不可再更改。
alStaticFinalVar.add("aaa"); //正确,容器变量本身没有变化,但存放内容发生了变化。这个规则是非常常用的,有很多用途。
alStaticFinalVar.add("bbb"); //正确,容器变量本身没有变化,但存放内容发生了变化。这个规则是非常常用的,有很多用途。
System.out.println("-------------值处理后----------\r\n");
System.out.println("strStaticFinalVar=" + strStaticFinalVar + "\r\n");
System.out.println("strStaticVar=" + strStaticVar + "\r\n");
System.out.println("strFinalVar=" + strFinalVar + "\r\n");
System.out.println("intStaticFinalVar=" + intStaticFinalVar + "\r\n");
System.out.println("integerStaticFinalVar=" + integerStaticFinalVar + "\r\n");
System.out.println("alStaticFinalVar=" + alStaticFinalVar + "\r\n");
}
public static void main(String args[]) {
new TestStaticFinal().test();
}
}
运行结果如下: -------------值处理前---------- strStaticFinalVar=aaa strStaticVar=null strFinalVar=null intStaticFinalVar=0 integerStaticFinalVar=8 alStaticFinalVar=[] -------------值处理后---------- strStaticFinalVar=aaa strStaticVar=哈哈哈哈 strFinalVar=null intStaticFinalVar=0 integerStaticFinalVar=8 alStaticFinalVar=[aaa, bbb] Process finished with exit code 0
通过static final修饰的容器类型变量中所“装”的对象是可改变的。这是和一般基本类型和类类型变量差别很大的地方
本节内容转载于:Java关键字final、static使用总结
对象和对象句柄的区别
1 | class value{ |
如以上代码:
- value v1和value v2声明了两个value类型的句柄
- 然后通过new操作符实例化了v1、v2这两个对象
注意:经过上面的操作,对象是确实已经被声明且实例化出来了,但是我们并不能直接的去操纵对象实例,你可能会问,v1和v2不就是对象了吗?不是的,v1和v2只是两个标识符,代表的是两个指向对象实例的句柄,我们通过句柄访问对象实例,使用v1.i时对v1执行的对象的i进行赋值就是这个道理。它们指向的对象我们实际上是看不到的。
举个比较贴近生活的浅显易懂的例子:就好比遥控器和电视的关系,电视是对象实例,而遥控器就是这个对象的句柄,我们通过遥控器来控制电视的音量或者换台等等,电视一直摆在那,我们直接控制不了(不要钻牛角尖),而遥控器我们可以随意拿来拿去,通过它来控制电视的相关内容。另外,遥控器可以独立于电视机而存在,即句柄可以被声明出来,只是没有连接对象而已,比如:String str;,这就是声明了一个还未连接任何对象的String类型的句柄。
这样解释应该就懂了,value v1和value v2是声明了两个value类型的变量(句柄),还没有与任何对象关联,new value()则真正的产生了一个value类型的对象,使用value v1=new value();是将句柄与对象进行了绑定操作。