集合
Java的集合类被定义在java.util包中,主要有4种集合,包括List, Queue, Set, Map。
List 可重复
List是一个非常常用的数据类型,是有序Collection,它的三个实现类分别为ArrayList,LinkedList和Vector。
- ArrayList:基于数组实现的List,增删慢,查询快,线程不安全
- Vector:基于数组实现,增删慢,查询快,线程安全
- LinkedList:基于双链表实现,增删快,查询慢,线程安全
Queue
- ArrayBlockingQueue:基于数组实现的有界阻塞队列
- LinkedBlockingQueue:基于链表实现的有界阻塞队列
- PriorityBlockingQueue:支持优先级排序的无界阻塞队列
- DelayQueue:支持延迟操作的无界阻塞队列
- SynchronousQueue:用于线程同步的阻塞队列
- LinkedTransferQueue:基于链表实现的无界阻塞队列
- LinkedBlockingDeque:基于链表结构实现的双向阻塞队列
Set 不可重复
- HashSet:HashTable实现,无序
- TreeSet:二叉树实现,有序,自定义的数据类型必须实现Comparable接口,并覆写其中的compareTo方法,升序返回-1, 降序返回1
- LinkHashSet:HashTable实现数据存储,双向链表记录顺序
Map
HashMap:数组 + 链表存储数据,线程不安全。
如果需要满足线程安全的条件,可以使用Collections的synchronizedMap方法使HashMap具有线程安全的能力,或者使用ConcurrentHashMap。
HashMap的数据结构:内部是一个数组,数组中的每一个元素都是Entry的实例,包含四个属性:key,value,hash值和用于指向单向链表下一个元素的next。
常用的参数:
- capacity:当前数组的容量,默认为16,可以扩容,扩容后大小为原来的两倍
- loadFactor:负载因子,默认为0.75
- threshold:扩容的阀值,为capacity * loadFactor
HashMap在查询数据的时候,根据hash值可以直接找到数据所在的数组下标,但是之后需要遍历该数组下的整个链表来查询数据,因此需要O(n)的时间。
为了减少链表遍历的时间,Java8对HashMap进行了优化,将数据结构修改为数据 + 链表或数组 + 红黑树,在链表的元素超过8个之后,HashMap会将链表结构转化为红黑树结构以提高查询效率。
ConcurrentHashMap:分段锁实现,线程安全
concurrencyLevel参数表示并行级别:默认为16,在这种情况下最多支持16个线程并发执行写操作,该值在初始化可以设置,一旦初始化后不能修改。
HashTable:线程安全
线程安全,但是同一时刻只有一个线程能执行写操作,并发性不如ConcurrentHashMap。
TreeMap:基于二叉树实现
在使用TreeMap时,其key必须实现了Comparable接口或采用自定义的比较器,否则会抛出java.lang.ClassCastException。
LinkedListHashMap:使用链表保存插入顺序
异常分类及处理
在Java中,Throwable是所有错误或异常的父类,Throwable又可以分为Error和Exception。
Error是指Java运行错误,如果程序在启动时出现Error,则启动失败,如果在运行时出现Error则安全退出进程。Error出现的原因一般是系统内部错误或者资源耗尽,系统也不能在运行时动态处理错误,能做的只是记录错误的成因和安全退出。
Exception是指Java运行异常,即在程序运行中发生了人们不期待的事件,可以被Java异常处理机制处理。Exception分为RuntimeException(运行时异常)和CheckedException(检查异常)
- RuntimeException:指在Java虚拟机正常运行期间抛出的异常,RuntimeException可以被俘获被处理
- CheckedException:在编译阶段Java编译器会检查CheckedException异常并强制俘获并处理此类异常,如IOException,SQLException,ClassNotFoundException等
反射机制
反射机制是在程序运行过程中,对任意一个类都能获取其所有属性和方法,并且对任意一个对象都能调用其任意一个方法。这种动态获取类和对象的信息,以及动态调用对象的方法的功能称为Java的反射机制。
Java中的对象有两种类型:编译时类型和运行时类型。编译时类型是指在声明对象时所采用的类型,运行时类型是指给对象赋值时采用的类型。
Java的反射API
- Class类:用于获取类的属性,方法等信息
- Field类:表示类的成员变量,用于获取和设置类中的属性值
- Mathod类:表示类的方法,用于获取方法的描述信息或者执行某个方法
- Constructor类:表示类的构造方法
反射的步骤
- 获取想要操作的类的Class对象,该类是反射的核心
- 调用Class对象所对应的类中定义的方法,这是反射的使用阶段
- 使用反射API来获取并调用类的属性和方法等信息
获取Class对象的3种方法:
// 调用某个对象的getClass方法
Person p = new Person();
Class clazz = p.getClass();
// 调用某个类的class属性
Class clazz = Person.class();
// 调用Class类中的静态方法forName方法,这是最安全,性能最高的方法
Class clazz = Class.forName("fullClassPath"); // 全类名
在获得类的Class对象后,可以通过Class类中的方法获取并查看该类的方法和属性
// 获取Person类的Class对象
Class clazz = Class.forName("fullClassPath");
// 获取Person类的所有方法的信息
Method[] methods = clazz.getDeclaredMethods();
// 获取Person类所有成员的属性信息
Field[] fields = clazz.getDeclaredFields();
// 获取Person类所有构造方法的信息
Constructor[] constructors = clazz.getDeclaredConstructors();
创建对象的两种方法
// 使用Class对象的newInstance方法,该方法要求该Class对象对应的类有空参的构造函数
// 使用Class对象获取指定的Constructor对象,再调用Constructor对象的newInstance方法创建Class对象对应的实例
Class clazz = Class.forName("fullClassPath");
Constructor c = clazz.getDeclaredConstructor(String.class, String.class);
Person p = (Person) c.newInstance("大锤", "男");
Method的invoke方法
Class clz = Class.forName("fullClassPath");
Method method = clz.getMethod("setName", String.class);
Constructor constructor = clz.getConstructor();
Object object = constructor.newInstance();
method.invoke(object, "dachui");
注解
注解的概念
注解是(Annotation)Java提供的设置程序中元素的关联信息和元数据的方法,它是一个接口,程序可以通过反射获取指定程序中的元素的注解对象,从而通过该注解对象获取注解中的元数据信息。
标准元注解
元注解负责注解其他注解。
@Target
// 说明了注解所修饰的对象范围
@Retention
// 定义了该注解被保留的级别
// SOURCE:在源文件中被保留
// CLASS:在Class文件中被保留
// RUNTIME:在运行时被保留
@Documented
// 表明该注解应该被javadoc工具记录
@Inherited
// 表明某个被标注的类型是被继承的
注解处理器
定义注解接口
@Traget(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface FruitProvider {
// 供应商编号
public int id() default -1;
// 供应商名称
public String name() default "";
// 供应商地址
public String address() default "";
}
使用注解接口
public Apple {
// 使用注解接口
@FruoitProvider(id = 1, name = "陕西红富士", address = "陕西省西安市")
private String appleProvider;
// setter()...
// getter()...
}
定义注解处理器
public class FruitInfoUtil {
public static void getFruitInfo(Class<?> clazz) {
Field[] fields = clazz.getDeclaredFields();
for(Field field : fields) {
if(field.isAnnotaionPresent(FruitProvider.class)) {
FruitProvider fruitProvider = (FruitProvider) field.getAnnotion(FruitProvider.class);
System.out.println("编号:" + fruitProvider.id() + "地址:" + fruitProvider.address())
}
}
}
public static void main(String[] args) {
FruitInfoUtil.getFruitInfo(Apple.class);
}
}
内部类(编程思想补充)
定义在类内部的类称为内部类,按照定义的方法的不同,可以分为静态内部类,成员内部类,静态内部类和匿名内部类。
静态内部类
静态内部类可以访问外部类的静态变量和方法;在静态内部类中可以定义静态变量,方法,构造函数等。
Java集合类HashMap在内部维护了一个静态内部类Node数组用于存放元素,当Node数据对于使用者是透明的。像这种和外部类关系密切且不依赖外部类实例的类,可以使用静态内部类实现。
成员内部类
成员内部类不用定义静态方法和变量(final修饰的除外)。
局部内部类
定义在方法中的类叫做局部内部类,当一个类只需要在某个方法中使用时,可以通过局部类来优雅地实现。
匿名内部类
指通过继承一个父类或者实现一个接口的方式定义并使用的类
泛型
泛型的本质是参数化类型。
在不使用泛型的情况下,我们可以使用引用Object类来实现参数的任意化。
使用泛型的好处是在编译期就能够检查类型是否安全,同时所有强制性类型转换都是自动和隐式进行的,提高了代码的安全性和重用性。
泛型标记和泛型限定:E,T,K,V,N,?
| 序号 | 泛型标记 | 说明 |
|---|---|---|
| 1 | E-Element | 在集合中使用,表示在集合中存放的元素 |
| 2 | T-Type | 表示Java类 |
| 3 | K-Key | 表示键 |
| 4 | V-Value | 表示值 |
| 5 | N-Number | 表示数据类型 |
| 6 | ? | 表示不确定的Java类型 |
类型擦除
在编码阶段才同泛型时加上的类型参数,会被编译器在编译时去掉,这个过程叫做类型擦除。
首先,查找用来替换类型参数的具体类(一般为Object),如果定义了类型参数的上界,则使用该上界,然后将代码中所有的类型参数都替换成具体的类。
序列化(待补充原理)
类要实现序列化功能,只需实现java.io.Serializable接口即可。
序列化并不保存以及transient修饰的变量
可以使用基于JDK原生的ObjectOutputStream和ObjectInputStream实现对象的序列化和反序列化,并调用writeObject和readObject方法实现自定义序列化策略。