Java基础
字符型常量和字符串常量
- 字符常量时单引号引起的一个字符,字符串常量时双引号引起的若干字符。
- 字符常量相当于一个整型值,可以参加表达式运算,字符串常量代表一个地址值。
- 字符常量只占2字节,字符串常量占若干字节。
重载和重写的区别
重载:是同样的一个方法能够根据输入数据的不同,做出不同处理。重载是同一个类中多个同名方法根据不同的传参执行不同的逻辑处理。
重写:是当子类继承自父类的相同方法,输入数据一样,但是要做出的和父类不一样的响应时,就要重写父类方法。重写是子类堆父类的重新改造,外部样子不能改变,但是可以改变内部逻辑。
以最常见的构造器为例,构造器不能被重写(override),但是可以被重载(overload),也就是经常看见一个类中有很多构造器的情况。
成员变量与局部变量
- 语法形式上,成员变量是属于类的,而局部变量是方法中定义的变量或是方法的参数。成员变量可以被
static
、private
、public
等修饰符修饰,而局部变量是不能被访问修饰符锁修饰的,但是他们都可以被final
关键字修饰。 - 存储方式上,成员变量如果是使用
static
关键字修饰的,那么这个成员变量就是属于类的,反之是属于实例的。对象存于堆内存,如果局部变量类型为基本数据类型,那么存储在栈内存,如果为引用数据类型,那存放的是指向堆内存对象的引用或是指向常量池中的地址。 - 生存时间上,成员变量是对象的一部分,它随着对象的创建而存在,而局部变量随着方法的调用的自动创建和消失。
- 成员变量如果没有赋初始值的话,会自动以类型的默认值而复制;而局部变量则不会自动赋值。
==和equals()
==:判断两个对象的内存地址是不是相等。
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
类型的对象时,虚拟机会再常量池中查找是否有已存在的值相同对象,若有则把它赋给当前引用,没有的话则重新创建一个。
hashcode()和equals()
你知道为什么重写equals()
方法必须重写hashcode()
方法吗?
先介绍下hashcode
:hashcode()
的作用是获取一个int整数即哈希码,也称为散列码。哈希码是确定对象在哈希表中的索引位置,Java中的所有类都包含该函数。
哈希码在HashSet
中应用:当把对象加入HashSet
时,HashSet
会先计算对象的hashcode
来判断对象加入的位置,同时也会与该位置其他加入对象的hashcode
作比较,若没有相符的hashcode
则会假设对象没有重复出现,否则会调用equals()
方法来检查哈希码相同的对象内容是否相同,若内容也相同,HashSet
就不会让其成功加入;否则的话就会重写散列到其他位置。通过hashcode
大大减少了equals
的调用次数,提高执行效率。这也回答了开头的问题。
hashcode()
和equals()
相关规定:
- 若两个对象相等,则
hashcode
一定是相同的,调用equals()
也都是返回true - 两个对象有相同的
hashcode
,但它们不一定相等 - 因此
equals()
覆盖的地方,hashcode()
方法也必须覆盖。 hashcode()
默认是对堆上的对象产生独特值,如果没有重写hashcode()
,则该class的两个对象无论如何都不会相等。
transient关键字
对于不想进行序列化的变量,使用transient
关键字修饰。当对象被反序列化时,被transient
关键字修饰的变量值不会被持久化和恢复。transient
只能修饰变量,不能修饰方法和类。
BIO、NIO和AIO
- BIO(Blocking I/O,同步阻塞I/O模式),数据的读取写入必须阻塞在一个线程内等待其完成。让每个连接专注于自己的I/O并且编程模式简单,不用过多考虑系统加载、限流等问题,但是连接数非常大时就无能为力了。
- NIO(Non-blocking/New I/O,同步非阻塞I/O模式),支持面向缓冲的,基于通道的I/O操作方法。阻塞模式使用就像传统中的支持一样,较为简单,但是性能和可靠性不好,而非阻塞模式正好与之相反,对于高负载、高并发的应用程序,应使用NIO模式开发。
- AIO(Asynchronous I/O,异步非阻塞模式),异步I/O是基于回调机制实现的,也就是应用操作之后会直接返回,而不是阻塞在那里,当后台处理完成后,操作系统会通知相应的线程进行后续的操作。
Java集合
ArrayList和LinkedList
- 线程安全
ArrayList
和LinkedList
都是不同步的,也就是线程不安全,vector
是同步的,线程安全。 - 底层数据结构
ArrayList
底层使用的是Object
数组,LinkedList
底层使用的是双向链表。 - 插入和删除元素
ArrayList
采用数组存储,所以插入和删除受元素位置影响;LinkedList
采用链表存储,所以插入和删除元素时受到元素位置影响。 - 快速随机访问
快速随机访问是通过元素的序号快速获取元素对象,通过底层数据就够就知道了,ArrayList
支持,LinkedList
不支持。 - 内存空间占用
ArrayList
的空间主要浪费在了list列表的结尾会预留一定的容量空间,而LinkedList
主要花费在每一个元素都要比ArrayList
更多的空间,因为存前驱后继节点及数据等。
HashMap和HashTable
- 线程安全
HashMap
是线程不安全的,而HashTable
是线程安全的,因为HashTable
的内部实现都加了synchronized
修饰。 - 效率
因为上述线程安全的问题,HashMap
效率也就更高一点。 - Null
HashMap
中可以把null
作为键或值,而HashTable
不支持。 - 扩容
若创建时未指定容量,HashMap
初始为 16 16 16,每次扩容为2倍,HashTable
初始为 11 11 11,并且每次扩容为 2 n + 1 2n+1 2n+1。
若创建时指定了容量,HashMap
会将其扩充至 2 2 2的幂次方大小,HashTable
会直接使用给定的容量。 - 底层数据结构
HashMap
jdk1.8以后,当链表长度超过阈值(默认8)时,会转换为红黑树,以减少搜索时间,而HashTable
没有这样的机制。
(
插播反爬信息)博主CSDN地址:https://wzlodq.blog.csdn.net/
HashMap
的长度为什么是2的幂次方?
为了能让HashMap
减少碰撞高效存储,要尽量把数据分配均匀。首先哈希值的取值范围很大,有将近40亿的空间,内存时放不下的,换言之,哈希值并不能直接使用,需要对数组的长度进行取模,得到的余数就是对应的数组下标。
而设计成2的幂次就是因为如果取余操作中除数是2的幂次的话,就等价于与其除数减一的与操作,也就是说 h a s h % l e n g t h = ( l e n g t h − 1 ) & h a s h hash\%length=(length-1)\&hash hash%length=(length−1)&hash,而采用二进制位运算可以大大提高效率,这也就是为什么HashMap
的长度是2的幂次方。
HashMap底层实现
JDK1.8之前:
JDK1.8之前HashMap
底层是数组和链表结合在一起使用也就是链表散列,HashMap
通过key的hashcode经过扰动函数处理后得到hash值,然后通过 ( n − 1 ) & h a s h (n-1)\&hash (n−1)&hash判断当前元素存放的位置,如果当前位置存在元素的话,就判断该元素与要存入的元素的hash值以及是否相同,若相同则直接覆盖,否则通过拉链法来解决冲突。所谓扰动函数指的就是HashMap
的hash方法。使用hash方法之后可以减少碰撞。
JDK1.8之后:
优化了哈希冲突的解决方法,当链表长度大于阈值(默认为8)时,会将链表转换为红黑树,以减少搜索时间。
comparable和comparator
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
- List
①ArrayList:Object数组
②Vector:Object数组
③LinkedList:双向链表 - Set
①HashSet:无序唯一,底层采用HashMap保存元素
②TreeSet:有序唯一,红黑树(即自平衡的排序二叉树)
Map
- HashMap:jdk1.8前时数组+链表,jdk1.8后大于阈值(默认8)则转链表为红黑树
- LinkedHashMap:继承自HashMap,增加了双向链表
- HashTable:数组+链表
- TreeMap:红黑树
Java多线程
JVM
原创不易,请勿转载(
本不富裕的访问量雪上加霜)
博主首页:https://wzlodq.blog.csdn.net/
微信公众号:唔仄lo咚锵
如果文章对你有帮助,记得一键三连❤
转载:https://blog.csdn.net/qq_45034708/article/details/115298556