1 . 静态与成员
静态变量(类变量):由static修饰的变量,实质就是全局变量
成员变量(实例变量):描述一个类属性的变量
区别:
静态变量 成员变量 所属 类 实例对象 生命周期 随着类消失 随着类实例化生成,实例对象消失而消失 调用方式 类名(或者对象名).变量名 对象名.变量名 储存方式 静态区 堆区 public class Demo { public static void main(String[] args) { //调用静态变量 S.a = 10; System.out.println(S.a); //调用成员变量 S B = new S(); B.b = 20; System.out.println(B.b); } } class S{ static int a;//静态变量 int b;//成员变量 }
静态方法:
成员方法:
区别:
静态方法 成员方法 所属 属于类 属于实例对象 访问 静态方法只能访问静态变量 静态变量实例变量都能访问 调用 类名(或者对象名).方法名 对象名.方法名 public class Dome2 { public static void main(String[] args) { //调用静态方法 Methods.method1(); //调用实例方法 Methods a = new Methods(); a.method2(); } } class Methods{ static void method1(){//静态方法 System.out.println("Hello"); } void method2(){ System.out.println("World"); } }
2 .==与equals
- == :
- 比较基本数据类型(int , byte,double等)比较的的是他们的大小
public class Equals { public static void main(String [] args){ int a = 10; int b = 10; System.out.println(b == a); double c = 11; double d = 9; System.out.pritnln(c == d); } } // 结果是true和false
比较引用数据类型(类,接口,数组等)会比较内存地址
public class Equals { public static void main(String [] args){ String a = new String("abc"); String b = new String("abc"); System.out.println(a == b);// 结果是false,因为a,b两个地址不一样 String c = new String("123"); String d = c; System.out.println(c == d);// 结果是true,两个地址一样 Dome A = new Dome(2); Dome B = new Dome(2); System.out.println(A == B); // 结果是false } } class Dome{ public int t; Dome(int t){ this.t = t; } }
但是
public class Equals { public static void main(String [] args){ String a = "abc"; String b = "abc"; System.out.println(a == b);// 结果是true } }
为什么会出现这种情况?
查资料发现,比较基本数据类型时,也是比较地址,只不过基本数据类型是作为常量,储存在方法区的常量池中,常量池中的数据以一种集合set方式存储数据,即每种数据个数只有一个,所以一个常量只会对应一个地址。字符串String也同理,数据“abc”,在常量池中只有一个地址,所以a == b 结果是true.
并且,我们可以解释
public class Equals { public static void main(String [] args){ String a = "abc"; String b = new String("abc"); System.out.println(a == b); // 结果是false 因为a是作为常量储存在方法区的常量池中,b是String类实例化的一个对象,对象是存储在堆区,与方法区不一样,== 比较的是两个的地址,故结果是false } }
- equals:
- 不能比较基本数据类型(int ,double等)
- 可以比较引用数据类型(比较地址)
equals是属于Object类的一个方法(查阅api帮助文档可知Object类是是所有类的父类,所有的类直接或间接的都继承了Object类)
// Object类中的equals方法 public boolean equals(Object obj){ return (this == obj); }
public class Equals { public static void main(String [] args){ String a = new String("123"); String b = new String("123"); System.out.println(a == b); System.out.println(a.equals(b)); Dome A = new Dome(1); Dome B = new Dome(1); System.out.println(A == B); System.out.println(A.equals(B)); } } class Dome{ public int t; Dome(int t){ this.t = t; } }
结果:
不是说Object类中的equals方法,也是用==比较的,那为什么第二个结果是true?
又经过查资料发现,原来字String类中,equals方法被重写了(重写是子类对父类的允许访问的方法的实现过程进行重新编写, 返回值和形参都不能改变。即外壳不变,核心重写!)从而达到覆盖父类原方法的目的。
// String 类中的equals方法 (百度) public boolean equals(Object anObject) { if (this == anObject) { return true; } if (anObject instanceof String) { String anotherString = (String)anObject; int n = value.length; if (n == anotherString.value.length) { char v1[] = value; char v2[] = anotherString.value; int i = 0; while (n-- != 0) { if (v1[i] != v2[i]) return false; i++; } return true; } } return false; }
可以看出在String类中,equals方法不再是单纯的比较两个String的地址,同样也比较两个对象的内容,如果两个对象的内容相同,也会返回true。
因此,在许多类中,equals或多或少的进行了重写,从而达到比较两个对象的属性内容是否相同。
如果是自己写的类,没有重写equals,会默认是Object类中的==比较。
3 . equals 与hashcode
Hash:一般翻译做散列、杂凑,或音译为哈希,是把任意长度的输入(又叫做预映射pre-image)通过散列算法变换成固定长度的输出,该输出就是散列值。这种转换是一种压缩映射,也就是,散列值的空间通常远小于输入的空间,不同的输入可能会散列成相同的输出,所以不可能从散列值来确定唯一的输入值。简单的说就是一种将任意长度的消息压缩到某一固定长度的消息摘要的函数。(百度百科)
简单来说就是,一个事物value,通过hash算法,从而映射到一个key值(哈希值)。就像函数一样,一个x变量通过多种变换得到一个y,这个y就是哈希值。
hash表:就是所有key值的一个集合。
hashcode方法:也是Object类中的一个方法,可以得到一个对象的哈希值
// csdn hashCode 的常规协定是: 在 Java 应用程序执行期间,在同一对象上多次调用 hashCode 方法时,必须一致地返回相同的整数,前提是对象上 equals 比较中所用的信息没有被修改。从某一应用程序的一次执行到同一应用程序的另一次执行,该整数无需保持一致。 如果根据 equals(Object) 方法,两个对象是相等的,那么在两个对象中的每个对象上调用 hashCode 方法都必须生成相同的整数结果。 以下情况不 是必需的:如果根据 equals(java.lang.Object) 方法,两个对象不相等,那么在两个对象中的任一对象上调用 hashCode 方法必定会生成不同的整数结果。但是,程序员应该知道,为不相等的对象生成不同整数结果可以提高哈希表的性能。 实际上,由 Object 类定义的 hashCode 方法确实会针对不同的对象返回不同的整数。(这一般是通过将该对象的内部地址转换成一个整数来实现的,但是 JavaTM 编程语言不需要这种实现技巧。) 当equals方法被重写时,通常有必要重写 hashCode 方法,以维护 hashCode 方法的常规协定,该协定声明相等对象必须具有相等的哈希码。
// 规定 1、如果两个对象equals相等,那么这两个对象的HashCode一定也相同 2、如果两个对象的HashCode相同,不代表两个对象就相同,只能说明这两个对象在散列存储结构中,存放于同一个位置
hashcode的作用是方便检索比较。一般的遍历用equals比较对象属性效率低下,所以可以通过先对对象hash,转换成哈希值,再从哈希值相等的当中进行equals比较。
所以当equals被重写后,应该重写hashcode方法,已达到两个对象equals相等,那么这两个对象的HashCode一定也相同,的规定。
public class Hash { public static void main(String [] args){ Dome a = new Dome(10); Dome b = new Dome(10); System.out.println(a.hashCode()); System.out.println(b.hashCode()); System.out.println(a.equals(b)); } } class Dome{ public int t; Dome(int t){ this.t = t; } //重写equals方法 public boolean equals(Object dome) { if (this == dome) { return true; } //判断dome是不是Dome的实例对象 if (dome instanceof Dome) { //向下转型 Dome dome1 = (Dome) dome; if (this.t == dome1.t) { return true; } else { return false; } } return false; } }
可以看出虽然equals相等,方式哈希值不相等,不符合规定。
// 重写后 public class Hash { public static void main(String [] args){ Dome a = new Dome(10); Dome b = new Dome(10); System.out.println(a.hashCode()); System.out.println(b.hashCode()); System.out.println(a.equals(b)); } } class Dome{ public int t; Dome(int t){ this.t = t; } //重写equals方法(仿照String类写的) public boolean equals(Object dome) { if (this == dome) { return true; } //判断dome是不是Dome的实例对象 if (dome instanceof Dome) { //向下转型 Dome dome1 = (Dome) dome; if (this.t == dome1.t) { return true; } else { return false; } } return false; } //重写hashcode public int hashCode(){ return this.t % 1000; } }