重载:是同样的一个方法能够根据输入数据的不同,做出不同处理。重载是同一个类中多个同名方法根据不同的传参执行不同的逻辑处理。 重写:是当子类继承自父类的相同方法,输入数据一样,但是要做出的和父类不一样的响应时,就要重写父类方法。重写是子类堆父类的重新改造,外部样子不能改变,但是可以改变内部逻辑。
以最常见的构造器为例,构造器不能被重写(override),但是可以被重载(overload),也就是经常看见一个类中有很多构造器的情况。
static
、private
、public
等修饰符修饰,而局部变量是不能被访问修饰符锁修饰的,但是他们都可以被final
关键字修饰。static
关键字修饰的,那么这个成员变量就是属于类的,反之是属于实例的。对象存于堆内存,如果局部变量类型为基本数据类型,那么存储在栈内存,如果为引用数据类型,那存放的是指向堆内存对象的引用或是指向常量池中的地址。==:判断两个对象的内存地址是不是相等。
equals():判断两个对象是不是同一个对象。
equals()
方法时,则通过equals()
比较的是该类的两个对象,这种情况等价于==。equals()
方法时,一般我们会通过比较两个对象的内容来判断是否相等。public class Car {
public static void main(String[] args) {
String a="一键三连";//常量池中
String b="一键三连";
String c=new String("一键三连");//引用
String d=new String("一键三连");
System.out.println(a==b);//true,指向常量池同一个地址
System.out.println(c==d);//false,两个不同的引用地址
System.out.println(a.equals(b));//true,判断内容
System.out.println(c.equals(d));//true
}
}
注意:
String
中的equals()
方法是被重写过的,因为object
中的equals()
方法比较的是对象的内存地址,而String
中的equals()
方法是比较对象的值。String
类型的对象时,虚拟机会再常量池中查找是否有已存在的值相同对象,若有则把它赋给当前引用,没有的话则重新创建一个。你知道为什么重写equals()
方法必须重写hashcode()
方法吗?
先介绍下hashcode
:hashcode()
的作用是获取一个int整数即哈希码,也称为散列码。哈希码是确定对象在哈希表中的索引位置,Java中的所有类都包含该函数。
哈希码在HashSet
中应用:当把对象加入HashSet
时,HashSet
会先计算对象的hashcode
来判断对象加入的位置,同时也会与该位置其他加入对象的hashcode
作比较,若没有相符的hashcode
则会假设对象没有重复出现,否则会调用equals()
方法来检查哈希码相同的对象内容是否相同,若内容也相同,HashSet
就不会让其成功加入;否则的话就会重写散列到其他位置。通过hashcode
大大减少了equals
的调用次数,提高执行效率。这也回答了开头的问题。
hashcode()
和equals()
相关规定:
hashcode
一定是相同的,调用equals()
也都是返回truehashcode
,但它们不一定相等equals()
覆盖的地方,hashcode()
方法也必须覆盖。hashcode()
默认是对堆上的对象产生独特值,如果没有重写hashcode()
,则该class的两个对象无论如何都不会相等。对于不想进行序列化的变量,使用transient
关键字修饰。当对象被反序列化时,被transient
关键字修饰的变量值不会被持久化和恢复。transient
只能修饰变量,不能修饰方法和类。
ArrayList
和LinkedList
都是不同步的,也就是线程不安全,vector
是同步的,线程安全。ArrayList
底层使用的是Object
数组,LinkedList
底层使用的是双向链表。ArrayList
采用数组存储,所以插入和删除受元素位置影响;LinkedList
采用链表存储,所以插入和删除元素时受到元素位置影响。ArrayList
支持,LinkedList
不支持。ArrayList
的空间主要浪费在了list列表的结尾会预留一定的容量空间,而LinkedList
主要花费在每一个元素都要比ArrayList
更多的空间,因为存前驱后继节点及数据等。
HashMap
的长度为什么是2的幂次方?
为了能让HashMap
减少碰撞高效存储,要尽量把数据分配均匀。首先哈希值的取值范围很大,有将近40亿的空间,内存时放不下的,换言之,哈希值并不能直接使用,需要对数组的长度进行取模,得到的余数就是对应的数组下标。
而设计成2的幂次就是因为如果取余操作中除数是2的幂次的话,就等价于与其除数减一的与操作,也就是说
,而采用二进制位运算可以大大提高效率,这也就是为什么HashMap
的长度是2的幂次方。
JDK1.8之前:
JDK1.8之前HashMap
底层是数组和链表结合在一起使用也就是链表散列,HashMap
通过key的hashcode经过扰动函数处理后得到hash值,然后通过
判断当前元素存放的位置,如果当前位置存在元素的话,就判断该元素与要存入的元素的hash值以及是否相同,若相同则直接覆盖,否则通过拉链法来解决冲突。所谓扰动函数指的就是HashMap
的hash方法。使用hash方法之后可以减少碰撞。
JDK1.8之后: 优化了哈希冲突的解决方法,当链表长度大于阈值(默认为8)时,会将链表转换为红黑树,以减少搜索时间。
comparable
接口出自java.lang
包,它有一个compateTo(Object obj)
方法用来排序。comparator
接口出自java.util
包,他有一个compare(Object obj1,Object obj2)
方法用来排序。 public static void main(String[] args) {
ArrayList<Integer> l = new ArrayList();
l.add(2);
l.add(1);
l.add(3);
System.out.println("===原始===");
System.out.println(l);
System.out.println("===自然排序===");
Collections.sort(l);
System.out.println(l);
System.out.println("===定制排序===");
Collections.sort(l, new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
return o2.compareTo(o1);
}
});
System.out.println(l);
}
/*运行结果如下:
===原始===
[2, 1, 3]
===自然排序===
[1, 2, 3]
===定制排序===
[3, 2, 1]
*/
public class Car implements Comparable<Car>{
private String name;
private double price;
public Car(String name, double price) {
this.name = name;
this.price = price;
}
public String getName() {
return name;
}
public double getPrice() {
return price;
}
@Override
public String toString() {
return "Car{" +
"name='" + name + '\'' +
", price=" + price +
'}';
}
@Override
public int compareTo(Car o) {
if(this.price==o.getPrice()) //价格相同 名字升序
return this.name.compareTo(o.getName());
else if(this.price>o.getPrice())return -1;
else return 1; //价格降序
}
public static void main(String[] args) {
ArrayList<Car> l =new ArrayList();
l.add(new Car("C",10));
l.add(new Car("A",10));
l.add(new Car("C",15));
Collections.sort(l);
System.out.println(l);
}
}
/*运行结果如下:
[Car{name='C', price=15.0}, Car{name='A', price=10.0}, Car{name='C', price=10.0}]
*/
Collection
Map