1. 九种基本数据类型的大小,以及他们的封装类
2. Switch能否用string做参数?
在Java中,switch后面的括号里放的参数类型只能是int类型,虽然说放入char,byte,short类型也不会报错,但其实是因为char,byte和short类型可以自己转化(宽化)为int类型,实际上最后操作的还是int类型。
原理:在Java的9种基本类型中,boolean和void类型不能进行转换。大小较小的类型向大小较大的类型转换叫宽化(如char转int),宽化会自动在变量前面补零且是安全的,因此可以自动转换。大小较大的类型向大小较小的类型转化叫窄化(如double转int),窄化不能自动转换,必须使用强制类型转换,如:(int)double。
补充:除了int类型外还支持枚举类型。另外在jdk1.7中有了新的特性,支持String类型。
3. equals与==的区别
java中的数据类型,可分为两类:
1.基本数据类型,也称原始数据类型。byte,short,char,int,long,float,double,boolean
他们之间的比较,应用双等号(==),比较的是他们的值。
2.复合数据类型(类)
当他们用(==)进行比较的时候,比较的是他们在内存中的存放地址,所以,除非是同一个new出来的对象,他们的比较后的结果为true,否则比较后结果为false。 JAVA当中所有的类都是继承于Object这个基类的,在Object中的基类中定义了一个equals的方法,这个方法的初始行为是比较对象的内存地址,但在一些类库当中这个方法被覆盖掉了,如String,Integer,Date在这些类当中equals有其自身的实现,而不再是比较类在堆内存中的存放地址了。
对于复合数据类型之间进行equals比较,在没有覆写equals方法的情况下,他们之间的比较还是基于他们在内存中的存放位置的地址值的,因为Object的equals方法也是用双等号(==)进行比较的,所以比较后的结果跟双等号(==)的结果相同。
4.Object有哪些公用方法?
Object中的public方法如图所示,简单介绍下每个方法:
- Object(),默认构造函数,略
- getClass(),native和final方法,返回此对象的运行时类
- hashCode(),native方法,返回此对象的哈希码数值
- equals(Object),详见上一个问题
- toString(),返回一个代表该对象的字符串,此处返回:“类名@16进制哈希码”
- notify(),native和final方法,唤醒在此对象监视器上等待的单个线程
- notifyAll(),native和final方法,唤醒在此对象监视器等待上的所有线程
- wait(),该方法有3个重载,作用是让当前线程处于等待状态,直到其他线程调用此对象的 notify() 方法或 notifyAll() 方法,或超过指定时间,或其他线程中断当前线程。
notify()和notifyAll()都是Object对象用于通知处在等待该对象的线程的方法。
void notify(): 唤醒一个正在等待该对象的线程。
void notifyAll(): 唤醒所有正在等待该对象的线程。
两者的最大区别在于:
notifyAll使所有原来在该对象上等待被notify的线程统统退出wait的状态,变成等待该对象上的锁,一旦该对象被解锁,他们就会去竞争。
notify他只是选择一个wait状态线程进行通知,并使它获得该对象上的锁,但不惊动其他同样在等待被该对象notify的线程们,当第一个线程运行完毕以后释放对象上的锁,此时如果该对象没有再次使用notify语句,即便该对象已经空闲,其他wait状态等待的线程由于没有得到该对象的通知,继续处在wait状态,直到这个对象发出一个notify或notifyAll,它们等待的是被notify或notifyAll,而不是锁。
5. Java的四种引用,强弱软虚,用到的场景
1. 强引用
指通过普通方式创建一个对象,并把这个对象赋给一个引用变量。只要还有引用变量指向,就不会被JVM回收,即使在内存不足时。
2. 软引用
通过SoftReference实现。 和强引用类似,区别是当内存不足时会被JVM回收。
1. public static void main(String[] args) {
2. SoftReference<User>[] users = new SoftReference[16910];
3.
4. for (int i = 0; i < users.length; i++) {
5. users[i] = new SoftReference<User>(new User("name" + i));
6. }
7.
8. System.out.println(users[0].get());
9. System.out.println(users[6].get());
10.
11. System.gc(); // 垃圾回收
12. System.runFinalization(); // 执行析构方法
13.
14. System.out.println();
15.
16. System.out.println(users[0].get());
17. System.out.println(users[6].get());
18. }
设置较小的JVM堆大小以模拟堆内存不足场景:-Xms2M -Xmx2M。1M会报Too small initial heap for new size specified错误。
输出:
com.john.jvm.User@39443f
com.john.jvm.User@1afae45
null
com.john.jvm.User@1afae45
3. 弱引用
通过WeakReference实现。 弱引用具有很强的不确定性,垃圾回收每次都会回收弱引用的对象。
1. public static void main(String[] args) {
2. User user = new User("name");
3. WeakReference<User> wr = new WeakReference<User>(user);
4. user = null; // 去除强引用
5. System.out.println(wr.get());
6.
7. System.gc(); // 垃圾回收
8. System.out.println(wr.get());
9. }
输出:
com.john.jvm.User@c1c428
null
4. 虚引用
通过PhantomReference实现。 软引用和弱引用可以单独使用,虚引用不能单独使用。虚引用的作用是跟踪对象被垃圾回收的状态。程序可以检测与虚引用关联的引用队列是否已经包含了指定的虚引用,从而了解虚引用的对象是否即将被回收。
本身对对象没有影响,类似于没有引用。对象甚至感觉不到引用的存在。如果一个对象只有一个虚引用存在,那么它就类似于没有引用存在。
1. public static void main(String[] args) {
2. User user = new User("name");
3. ReferenceQueue<User> rq = new ReferenceQueue<User>(); // 引用队列
4. PhantomReference<User> pr = new PhantomReference<User>(user, rq); // 创建一个虚引用,关联指定对象和队列。
5. user = null;
6. System.out.println(pr.get()); // 获取虚引用的对象
7. System.gc(); // 垃圾回收
8. System.runFinalization();
9. System.out.println((rq.poll() == pr)); // 取出引用队列的第一个对象与虚引用进行比较
10. }
输出:
null
true
6. Hashcode的作用
hashCode的存在主要是用于查找的快捷性,如Hashtable,HashMap等,hashCode是用来在散列存储结构中确定对象的存储地址的
7. ArrayList、LinkedList、Vector的区别
先说说相同点:三者都实现了List接口
再来说说不同的地方:
- LinkedList额外实现了Queue接口
- ArrayList和Vector底层是由数组实现,LinkedList底层是由双向链表实现
- 根据第2点,ArrayList和Vector具有更好的随机索引读取性能,LinkedList具有更好的插入删除元素性能
- ArrayList和LinkedList不是线程安全的,Vector是线程安全的(关键方法都加了synchronized关键字),因此Vector在性能上不如前两者
8. String、StringBuffer与StringBuilder的区别
String 字符串常量
StringBuffer 字符串变量(线程安全)
StringBuilder 字符串变量(非线程安全)
简要的说, String 类型和 StringBuffer 类型的主要性能区别其实在于 String 是不可变的对象, 因此在每次对 String 类型进行改变的时候其实都等同于生成了一个新的 String 对象,然后将指针指向新的 String 对象,所以经常改变内容的字符串最好不要用 String ,因为每次生成对象都会对系统性能产生影响,特别当内存中无引用对象多了以后, JVM 的 GC 就会开始工作,那速度是一定会相当慢的。
而如果是使用 StringBuffer 类则结果就不一样了,每次结果都会对 StringBuffer 对象本身进行操作,而不是生成新的对象,再改变对象引用。所以在一般情况下我们推荐使用 StringBuffer ,特别是字符串对象经常改变的情况下。而在某些特别情况下, String 对象的字符串拼接其实是被 JVM 解释成了 StringBuffer 对象的拼接,所以这些时候 String 对象的速度并不会比 StringBuffer 对象慢,而特别是以下的字符串对象生成中, String 效率是远要比 StringBuffer 快的:
String S1 = “This is only a” + “ simple” + “ test”;
StringBuffer Sb = new StringBuilder(“This is only a”).append(“simple”).append(“ test”);
你会很惊讶的发现,生成 String S1 对象的速度简直太快了,而这个时候 StringBuffer 居然速度上根本一点都不占优势。其实这是 JVM 的一个把戏,在 JVM 眼里,这个
String S1 = “This is only a” + “ simple” + “test”; 其实就是:
String S1 = “This is only a simple test”; 所以当然不需要太多的时间了。但大家这里要注意的是,如果你的字符串是来自另外的 String 对象的话,速度就没那么快了,譬如:
String S2 = “This is only a”;
String S3 = “ simple”;
String S4 = “ test”;
String S1 = S2 +S3 + S4;
这时候 JVM 会规规矩矩的按照原来的方式去做
在大部分情况下 StringBuffer > String
StringBuffer
Java.lang.StringBuffer线程安全的可变字符序列。一个类似于 String 的字符串缓冲区,但不能修改。虽然在任意时间点上它都包含某种特定的字符序列,但通过某些方法调用可以改变该序列的长度和内容。
可将字符串缓冲区安全地用于多个线程。可以在必要时对这些方法进行同步,因此任意特定实例上的所有操作就好像是以串行顺序发生的,该顺序与所涉及的每个线程进行的方法调用顺序一致。
StringBuffer 上的主要操作是 append 和 insert 方法,可重载这些方法,以接受任意类型的数据。每个方法都能有效地将给定的数据转换成字符串,然后将该字符串的字符追加或插入到字符串缓冲区中。append 方法始终将这些字符添加到缓冲区的末端;而 insert 方法则在指定的点添加字符。
例如,如果 z 引用一个当前内容是“start”的字符串缓冲区对象,则此方法调用 z.append("le") 会使字符串缓冲区包含“startle”,而 z.insert(4, "le") 将更改字符串缓冲区,使之包含“starlet”。
在大部分情况下 StringBuilder > StringBuffer
java.lang.StringBuilde
java.lang.StringBuilder一个可变的字符序列是5.0新增的。此类提供一个与 StringBuffer 兼容的API,但不保证同步。该类被设计用作 StringBuffer 的一个简易替换,用在字符串缓冲区被单个线程使用的时候(这种情况很普遍)。如果可能,建议优先采用该类,因为在大多数实现中,它比StringBuffer 要快。两者的方法基本相同。
10. HashMap和HashTable的区别
HashMap和HashTable都实现了Map接口,他们功能也相当类似,两者的主要区别如下:
- HashMap继承自AbstractMap类,而HashTable继承自Dictionary类
- HashMap不是线程安全的,HashTable是线程安全的(关键方法添加了synchronized关键字),因此HashTable效率相对较低
- HashMap允许key和value为null,HashTable不允许key和value为null