1. Java中length,length方法,size方法区别
length属性:用于获取数组长度。
length方法:用于获取字符串长度。
size方法:用于获取泛型集合有多少个元素。
2. isEmpty方法
isEmpty方法用来判断是否为空,很多类都有,比如String、Queue、Stack类。
3. Queue中 add/offer,element/peek,remove/poll方法
add 增加一个元索 如果队列已满,则抛出一个IIIegaISlabEepeplian异常
remove 移除并返回队列头部的元素 如果队列为空,则抛出一个NoSuchElementException异常
element 返回队列头部的元素 如果队列为空,则抛出一个NoSuchElementException异常
offer 添加一个元素并返回true 如果队列已满,则返回false
poll 移除并返问队列头部的元素 如果队列为空,则返回null
peek 返回队列头部的元素 如果队列为空,则返回null
4. Set用法
Set的特点:不能存储相同的元素。
5. static代码块
static代码块,进入程序后优先执行的一段代码,优先于构造方法,类加载时就会调用,仅执行一次。
6. 重写equals和hashcode方法
重写equals()方法就必须重写hashCode()方法主要是针对HashSet和Map集合类型。集合框架只能存入对象(对象的引用)。在向HashSet集合中存入一个元素时,HashSet会调用该对象(存入对象)的hashCode()方法来得到该对象的hashCode()值,然后根据该hashCode值决定该对象在HashSet中存储的位置。简单的说:HashSet集合判断两个元素相等的标准是:两个对象通过equals()方法比较相等,并且两个对象的HashCode()方法返回值也相等。如果两个元素通过equals()方法比较返回true,但是它们的hashCode()方法返回值不同,HashSet会把它们存储在不同的位置,依然可以添加成功。同样:在Map集合中,例如其子类Hashtable(jdk1.0错误的命名规范),HashMap,存储的数据是对,key,value都是对象,被封装在Map.Entry,即:每个集合元素都是Map.Entry对象。在Map集合中,判断key相等标准也是:两个key通过equals()方法比较返回true,两个key的hashCode的值也必须相等。判断valude是否相等equal()相等即可。
集合类都重写了toString方法。String类重写了equal和hashCode方法,比较的是值。
7. 序列化与反序列化
Java中对象的序列化是指将内存中的对象转换成以字节序列的形式表示,这些字节序列包含了对象的数据和信息,序列化后的对象可以被写到数据库、文件中或者网络传输,一般当我们使用缓存cache或远程调用rpc的时候,经常要让实体类实现Serializable接口。
反序列化就是将字节序列恢复成Java对象的过程 。
8. transient关键字
transient作用是让某些被修饰的成员属性变量不被序列化,比如类中的字段值可以根据其它字段推导出来 ,可以考虑使用关键字transient修饰,主要是为了节省存储空间。
9. 枚举enum
- 是什么:枚举是一个特殊的类,有实例字段、构造器和方法, 因此它是可拓展的。
- 何时用:定义固定常量集合的时候,等价于public static final 变量名,如下所示:
1 public enum Index { 2 ZERO(0), 3 ONE(1), 4 TWO(2); 5 6 private int index; 7 8 Index(int index) { 9 this.index = index; 10 } 11 12 public int getIndex() { 13 return index; 14 } 15 }
10. ThreadLocal局部变量实现线程同步
如果使用ThreadLocal管理变量,则每一个使用该变量的线程都获得该变量的副本, 副本之间相互独立,这样每一个线程都可以随意修改自己的变量副本,而不会对其他线程产生影响。
ThreadLocal 的使用:java并发编程 ThreadLocal使用
ThreadLocal 的原理:深入理解 ThreadLocal (这些细节不应忽略)
11. 原子类Automic
赋值一个变量,编译后会产生多个JVM语言指令,如果处于多线程情况下对于这个变量进行加减操作会导致数据不一致。为避免此问题, Java 引入了原子变量 Atomic。当一个线程正在操作一个原子变量时,即使其他线程也想要操作这个变量,类的实现中含有一个检查那步骤操作是否完成的机制。 基本上,操作获取变量的值,改变本地变量值,然后尝试以新值代替旧值。如果旧值还是一样,那么就改变它。如果不一样,方法再次开始操作。这个操作称为 Compare and Set (校对注:简称 CAS ,比较并交换的意思)。具体原理参见java并发包之原子类Automic
12. 线程池
New Thread的弊端如下:
- 每次新建对象性能差。
- 线程缺乏统一的管理,可能无限制的新建线程,相互竞争,可能占用过多的资源导致OOM。
- 缺乏更多功能,如定时执行、定期执行、线程中断。
Java提供的四种线程池的好处在于:
- 重用存在的线程,减少对象创建、消亡的开销,性能佳。
- 可有效控制最大并发线程数、提供系统资源的使用率,同时避免过多资源竞争。
- 提供定时执行、定期执行、单线程、并发数控制等功能。
Java通过Executors提供四种线程池,分别为:
newCachedThreadPool 创建一个可缓存线程池,可灵活回收空闲线程,若无可回收,则新建线程;
newFixedThreadPool 创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待;
newScheduledThreadPool 创建一个定时线程池,支持定时及周期性任务执行。
newSingleThreadExecutor 创建单个工作线程执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。
线程池不建议使用Executors去创建,而是通过ThreadPoolExecutor的方式,这样可以让写的同学更加明确线程池的运行规则,规避资源耗尽的风险。
Executors各个方法的弊端:
- newFixedThreadPool和newSingleThreadExecutor:
主要问题是堆积的请求处理队列可能会耗费非常大的内存。
- newCachedThreadPool和newScheduledThreadPool:
主要问题是线程数最大数是Integer.MAX_VALUE,可能会创建数量非常多的线程。
以ScheduledThreadPoolExecutor为例:
1 public class ScheduledThreadPoolTest { 2 public static void main(String[] args) throws InterruptedException { 3 // 创建大小为5的线程池 4 ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(5); 5 6 for (int i = 0; i < 3; i++) { 7 Task worker = new Task("task-" + i); 8 // 只执行一次 9 // scheduledThreadPool.schedule(worker, 5, TimeUnit.SECONDS); 10 // 周期性执行,每5秒执行一次 11 scheduledThreadPool.scheduleAtFixedRate(worker, 0,5, TimeUnit.SECONDS); 12 } 13 Thread.sleep(10000); 14 15 System.out.println("Shutting down executor..."); 16 // 关闭线程池 17 scheduledThreadPool.shutdown(); 18 boolean isDone; 19 // 等待线程池终止 20 do { 21 isDone = scheduledThreadPool.awaitTermination(1, TimeUnit.DAYS); 22 System.out.println("awaitTermination..."); 23 } while(!isDone); 24 System.out.println("Finished all threads"); 25 } 26 }
1 class Task implements Runnable { 2 private String name; 3 4 public Task(String name) { 5 this.name = name; 6 } 7 8 @Override 9 public void run() { 10 System.out.println("name = " + name + ", startTime = " + new Date()); 11 try { 12 Thread.sleep(1000); 13 } catch (InterruptedException e) { 14 e.printStackTrace(); 15 } 16 System.out.println("name = " + name + ", endTime = " + new Date()); 17 } 18 }
13. Java中自动装箱与拆箱(autoboxing and unboxing)
自动装箱就是自动将基本数据类型转换为包装器类型;
自动拆箱就是自动将包装器类型转化为基本数据类型。
1 Integer total = 99;//自动装箱 2 int totalprim = total;//自动拆箱
自动装箱拆箱的类型为八种基本类型:
14. Java中String、StringBuffer、StringBuilder
String:不可变长的字符序列;
StringBuffer:可变的字符序列,线程安全,效率低;
StringBuilder:可变的字符序列,线程不安全,效率高。
15. 向下转型和向上转型
为了共用一套代码:
1 public class PetFactory { 2 private PetFactory() { 3 } 4 public static Pet getPet(String type) throws Exception { 5 Pet pet = null; 6 switch (type) { 7 case "dog": 8 pet = new Dog(); 9 break; 10 case "cat": 11 pet = new Cat(); 12 break; 13 default: 14 throw new Exception(); 15 } 16 return pet; 17 } 18 } 19 public void example(String type){ 20 Pet pet = PetFactory.getPet(type); //向上转型 21 playWithPet(pet);//公共的 22 if(pet instanceOf Dog){ 23 Dog snoopy = (Dog) pet; //向下转型 24 snoopy.sitDown(); 25 } else { 26 Cat white = (Cat) pet; //向下转型 27 white.climb(); 28 } 29 }
16. String hashCode 方法选择数字31作为乘子
- 31是一个不大不小的质数,质数的特性(只有1和自己是因子)能够使得它和其他数相乘后得到的结果比其他方式更容易产成唯一性,降低哈希算法的冲突率;
- 31可以被 JVM 优化,31 * i = (i << 5) - i。
17. Java内部类访问外部类局部变量必须声明为final
编译后的内部类和外部类各一个class文件,内部类访问外部类局部变量实际是复制了一份,为了避免数据的不一致性,设置局部变量为final。
18. 类的加载
Class文件由类装载器装载后,在JVM中将形成一份描述Class结构的元信息,通过该元信息可以获知Class的结构信息:如构造函数,属性和方法等,Java允许用户通过元信息间接调用Class对象的功能。
在Java中,类装载器把一个类装入JVM中,要经过以下步骤:
- 装载:查找和导入Class文件;
- 链接:把类的二进制数据合并到JRE中; (a)校验:检查载入Class文件数据的正确性; (b)准备:给类的静态变量分配存储空间; (c)解析:将符号引用转成直接引用;
- 初始化:对类的静态变量,静态代码块执行初始化操作
19. 双亲委派模式
- 原理
类加载器有加载类的需求时,先请求父加载器帮忙加载,直到传到顶层启动类加载器,父加载不了再由子加载器加载;
- 使用原因
为了避免重复加载,父加载器加载过了子加载器就没必要再加载了,否则我们可以随时使用自定义的类代替Java核心API中的类型,好阔怕
20. 代理模式
- 为什么做代理
为了明确职责、增加扩展性(只需要丰富代理功能而不用修改自己方法的代码),把自己需要但是不适合自己做的东西交给代理来做;
比如常见的两类情况:
(a) 在一个类调用前和调用后搞些事情,比如权限控制、日志打印等;
(b) 客户端无法直接操作对象,需要通过网络来访问,可以建立远程对象的代理,像调用本地方法一样调用远程对象等