8种基础数据类型的大小、封装类
类型 | 封装类 | 位数 | 表现形式 |
---|---|---|---|
double | Double | 8位 | 0.0d |
float | Float | 4位 | 0.0f |
long | Long | 8位 | 0L |
int | integer | 4位 | 0 |
short | Shor | 2位 | (short) 0 |
byte | byte | 1位 | (byte) 0 |
char | Character | 2位 | null \u0000 |
boolean | Boolean | – | false |
基础数据类型、引用数据类型的区别
数据类型 | 所属 | 区别 |
---|---|---|
基础数据类型 | double、float、long、int、short、byte、char、boolean(8种基础类型) | 所有的简单数据类型不存在“引用”的概念,基础数据类型都是直接存储在内存栈上,数据北盛的值就是存储在栈空间里 |
引用数据类型 | 类、接口、数组(3种引用类型) | 引用类型继承了Object类(引用类型),按照Java内存储内向的内存模型来进行数据存储,使用Java内存堆和内存栈进行存储;“引用”是存储在有序的内存栈上,而对象本身的值存储在内存堆上 |
Java(OOP)面向对象的三个特征与定义
- 封装:可见性封装,set、get读写,将类的某些特征隐藏在类的内部,不允许外部程序直接访问,而是通过该类提供的方法来实现对隐藏信息的操作和访问。
- 继承:子类继承父类,可以得到父类的全部属性和方法(除了父类的构造方法),java中的多继承可以通过接口来实现。
- 多态:
- 编译时多态:通过方法的重载类实现。
- 运行时多态:通过方法的重写来实现。
重写和重载的含义与区别
- 重写:是子类覆盖父类方法,重新定义;
- 但是返回类型、参数、参数类型、抛出异常都和父类一致;
- 被覆盖的方法不能private,子类函数访问权限要大于等于父类;
- 子类无法覆盖父类的static方法或private方法。
- 重载:是一个类中,方法名相同;但是具有不同程度程度参数类型、不同的参数个数、不同的参数顺序。
Interface、abstract的区别
区别 | abstract(抽象类) | Interface(接口) |
---|---|---|
构造方法 | 可以包含 | 不能包含 |
不同成员变量 | 可以包含 | 不能包含 |
非抽象的普通方法 | 可以包含 | 不能包含 |
抽象方法访问类型 | public、protected、默认类型 | public |
静态方法 | 可以包含 | 不能包含 |
静态成员变量 | 可以包含、访问类型随意 | 可以包含、方位类型只能是public static final |
实现情况 | 一个类只能继承一个抽象类 | 一个类可以实现多个接口 |
static class、non static class的区别(Java多态的实原理)
区别 | 静态内部类(static class) | 非静态内部类(non static class) |
---|---|---|
指向外部的引用 | 不需要 | 需要 |
访问成员 | 不能访问外部类的非静态成员 只能访问外部类的静态成员 | 能够访问外部类的静态和非静态成员 |
/ | 一个非静态内部类不能脱离外部类实体被创建 | |
/ | 一个非静态内部类可以访问外部类的数据和方法,因为它就在外部内里 |
foreach、正常for循环效率对比
处理类型 | 效率for | |
---|---|---|
数组 | 效率大致相同 | 效率大致相同 |
链表 | 效率低 | 效率高 |
switch支持的参数类型
JDK版本 | 支持类型 |
---|---|
Jdk1.7以前 | byte、short、char、int |
Jdk1.7以后 | byte、short、char、int、整形、枚举型、boolean、字符串 |
equals 与 == 的区别
类型 | 区别 | 原因 |
---|---|---|
== | == 比较的是两个对象的地址 | == 比较的是变量内存(栈)中存放的对象的(堆)内存地址,用来判断两个对象的地址是否相同,即是否是指同一个对象;比较的是真正意义上的指针操作。 |
equals | equals 比较的是两个对象的内容 | equals 比较的是两个对象的内容是否相同,由于所有的类都继承自 java.lang.Object 类,所以适用于所有对象。如果没有对该方法进行覆盖的话,调用的依然是 Object 类中的方法,而 Object 中的 equals 方法返回的是 == 的判断。 |
Object类的公用方法
clone()、hashCode()、equals()、notify()、notifyAll()、wait()、getClass()、toString()、finalize()
Java的4种引用及其场景
引用类型 | 使用场景 |
---|---|
强引用 | 使用普遍的引用;内存空间不足时,一般垃圾回收器绝不会回收它 |
软引用 | 可用来实现内存敏感的高速缓存;内存空间不足时,就会回收这些对象的内存 |
弱引用 | 具有弱引用的对象,不管当前内存空间是否足够都会回收它的内存 |
虚引用 | 虚引用并不会决定对象的生命周期。如果一个对象持有虚引用,那么它就和没有任何引用一样,在任何时候都可能被垃圾回收器回收 |
String、StringBuffer、StringBuilder的区别
类型 | 属性 | 是否可变 | 线程是否安全 | 适用场景 |
---|---|---|---|---|
String | 字符串常量(final修饰,不可继承) | String对象一旦创建之后该对象不可更改 String str=“abc”; str=str+“de”; JVM又创建了一个新的名为str的对象,然后把原来的str的值和“de”加起来再赋值给新的str,而原来的str就会被JVM的垃圾回收机制(GC)给回收掉 | 线程安全 | 适用于少量的字符串操作的情况 |
StringBuffer | 字符串变量 | 对象可变 | 线程安全 | 适用多线程下在字符缓冲区进行大量操作的情况 |
StringBuilder | 字符串变量 | 对象可变 | 线程不安全 | 适用于单线程下在字符缓冲区进行大量操作的情况 |
hashcode的总结
什么是HashCode
- HashCode的存在主要是为了查找的快捷性,HashCode是用来在散列存储结构中确定对象的存储地址的
- 如果两个对象equals相等,那么这两个对象的HashCode一定也相同
- 如果对象的equals方法被重写,那么对象的HashCode方法也尽量重写
- 如果两个对象的HashCode相同,不代表两个对象就相同,只能说明这两个对象在散列存储结构中,存放于同一个位置
hashcode的作用
Java中的hashcode方法就是根据一定的规则将与对象相关的信息(比如:对象的存储地址、对象的字段等)映射成一个值(散列值),作用是降低equals的调用,实现存放的值不会重复。
Note:重写 equals 必须重写 hashcode 方法,equals 相等,hashcode 也必须相等。
一般对于存放到 Set 集合或者 Map 中键值对的元素,需要按需要重写 hashcode 和 equals 方法,以保证唯一性。
例如:HashSet存放多个对象,重写 equals 和 hashcode
- 对象相等,hashcode一定相同
- 对象不相等,hashcode可能相同
- hashcode相同的对象,不一定相等
- hashcode不相同的对象,一定不相等
为什么重载hashcode方法
- 重载时机:当类需要放在HashTable、HashMap、HashSet等hash结构的集合时需要重载hashcode。
- 为什么重载:好比HashMap是一个大内存块,内部有很多小内存块,小内存块里面是一系列的对象,可以利用hashcode来查找小内存块hashcode%size,所以当equals相等时,hashcode必须相等,而且如果是Object对象,必须重载equals和hashcode方法。
ArrayList、LinkedList、Vector的区别
类型 | 线程是否安全 | 数据存储结构 | 适用场景 | 可否扩容 | 所需内存 | 区别原因 |
---|---|---|---|---|---|---|
ArrayList | 线程不安全 | 数组 | 适合查询 | 可自动扩容50% | 少(存储每个索引的位置是实际的数据) | 基于索引(index)的数据结构,它使用索引在数组中搜索和读取数据是很快的。获取数据的时间复杂度是O(1)。 但是要插入、删除数据却是开销很大的,因为这需要重排数组中的所有数据,还需要更新索引(除了插入、删除数组的尾部)时间复杂度是O(n) |
LinkedList | 线程不安全 | 链表 | 适合插入、删除 | 没有扩容的机制 | 多(每个节点中存储的是实际的数据和前后节点的位置) | 插入、删除更快的。不像ArrayList一样需要改变数组的大小,也不需要在数组装满的时候要将所有的数据重新装入一个新的数组,插入或删除的时间复杂度仅为O(1)。 |
Vector | 线程安全 | 数组 | 适合查询 | 可自动扩容100% | 少(存储每个索引的位置是实际的数据) |
数组、链表在内存的存储
- 数组:ArrayList — 静态分配内存,内存连续;数组元素在栈区。
- 链表:LinkedList — 手持下一个元素的地址,动态分配内存,内存不连续,链表元素在堆区。
HashMap、HashTable、LinkedHashMap、TreeMap、ConcurrentHashMap的区别
类型 | 是否有序 | 是否可存null | 是否线程安全 | 是否同步 |
---|---|---|---|---|
HashMap | 无序 | 允许存null | 非线程安全 | 不同步 |
HashTable | 有序 | 不允许存null | 线程安全 | 同步(写入慢) |
LinkedHashMap | 有序(遍历比HashMap慢) | 允许存null | 非线程安全 | 不同步 |
TreeMap | 有序 | 不允许存null | 线程安全 | 同步 |
ConcurrentHashMap | 有序 | 不允许存null | 线程安全,适用于高并发 | 同步 |
HashMap的底层实现
详解查看:HashMap 原理解析
-
HashMap的get(key)方法:获取key的hash值,计算 hash&(n-1) 得到在链表数组中的位置 first=tab[hash&(n-1)],先判断first的key是否参数key值相等,不等就遍历后面的链表,找到的key值返回对应的value值。
public V get(Object key) { Node<K,V> e; return (e = getNode(hash(key), key)) == null ? null : e.value; } final Node<K,V> getNode(int hash, Object key) { Node<K,V>[] tab; Node<K,V> first, e; int n; K k; if ((tab = table) != null && (n = tab.length) > 0 && (first = tab[(n - 1) & hash]) != null) { if (first.hash == hash && // always check first node ((k = first.key) == key || (key != null && key.equals(k)))) return first; if ((e = first.next) != null) { if (first instanceof TreeNode) return ((TreeNode<K,V>)first).getTreeNode(hash, key); do { if (e.hash == hash && ((k = e.key) == key || (key != null && key.equals(k)))) return e; } while ((e = e.next) != null); } } return null; }
-
HashMap的put(key)方法:判断键值对数组tab[]是否为空或null,否者以默认大小resize();根据键值key计算hash值得到插入的数组索引 i,如果tab[i]==null,直接新建节点添加,否则判断当前数组中处理hash冲突的方式为链表或红黑树(check第一个节点类型即可),分别处理。
public V put(K key, V value) { return putVal(hash(key), key, value, false, true); } final V putVal(int hash, K key, V value, boolean onlyIfAbsent, boolean evict) { Node<K,V>[] tab; Node<K,V> p; int n, i; if ((tab = table) == null || (n = tab.length) == 0) n = (tab = resize()).length; if ((p = tab[i = (n - 1) & hash]) == null) tab[i] = newNode(hash, key, value, null); else { Node<K,V> e; K k; if (p.hash == hash && ((k = p.key) == key || (key != null && key.equals(k)))) e = p; else if (p instanceof TreeNode) e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value); else { for (int binCount = 0; ; ++binCount) { if ((e = p.next) == null) { p.next = newNode(hash, key, value, null); if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st treeifyBin(tab, hash); break; } if (e.hash == hash && ((k = e.key) == key || (key != null && key.equals(k)))) break; p = e; } } if (e != null) { // existing mapping for key V oldValue = e.value; if (!onlyIfAbsent || oldValue == null) e.value = value; afterNodeAccess(e); return oldValue; } } ++modCount; if (++size > threshold) resize(); afterNodeInsertion(evict); return null; }
-
构造hash表:如果不指明初始大小,默认大小为16(即Node数组大小16),如果Node[]数组中的元素达到(填充比*Node.length)重新调整HashMap大小,变为原来的2倍大小,此过程非常耗时。
HashTable、ConcurrentHashMap的区别
- HashTable:性能弱、迭代器一致性:强一致性(锁住整个map)
- ConcurrentHashMap:性能强、迭代器一致性:弱一致性(锁住操作功能块,提升性能)
ConcurrentHashMap的并发度
ConcurrentHashMap的并发度:是segment的大小,默认为16;意味着最多同时可以有16条线程操作ConcurrentHashMap。
try{}catch{}finally{},try里有return,finally{}是否执行
- 任何执行try或者catch中的return语句之前,都会先执行finally语句,如果finally存在的话。
- 如果finally中有return语句,那么程序就执行finally中的return返回,所以finally中的return是一定会被当做返回值返回,此时的返回值不是try或catch中保存的返回值;编译器把finally中的return实现为一个warning。
Excption、Error包结构
- Excption、Error都是Throwable的子类
- Exception:指出了合理的应用程序想要捕获的条件。
- Error:用于指示合理的用应程序不应该试图捕获的严重问题,大多数这样的错误都是异常条件。
OOM、SOF的发生的场景
- OutOfMemoryError:Java Heap溢出、虚拟机栈和本地方法栈溢出、运行时常量池溢出、方法区溢出。
- StackOverflowError:当程序递归太深而发生堆栈溢出时,抛出该错误。
IO、NIO的区别
IO | NIO |
---|---|
面向流 | 面向块(缓冲区 Buffer) |
阻塞 | 非阻塞 |
无 | 选择器(Selector) |
无 | 管道(Channel) |
少连接,大数据可用IO | 多连接,少数据可以用NIO |