目录
一、反射
类加载的区别:
RTTI:编译期完成类的加载(classLoader:加载-验证-准备-解析-初始化)
反射:(越过编译期)在运行期完成类的加载。
反射的应用场景:写代码的时候(编译期)不知道用哪个类,哪个方法。例如需要看实际的业务来选择操作类
怎么用?
需要先了解JVM类加载流程和内存结构:
【java源文件.java】==》
类编译器(将java源码编译为class文件)==》
类加载器(将class文件加载到JVM)
而class文件里包含了大量信息:属性、方法、字段、接口、父类、访问标记、常量池、版本号、魔数等
结论:因此反射的入口是 .class文件
怎么创建class对象
/**
* 示例:创建Class对象的3种方式
*/
@Test
public void test() throws Throwable{
// 方式一 类.class
Class personClazz = Person.class;
// 方式二 实例.getClass()
Person person = new Person();
Class personClazz1 = person.getClass();
// 方式三 Class.forName("类的全路径")
Class personClazz2 = Class.forName("com.yuyu.reflect.Person");
}
生成对象的步骤
RTTI:Person person = new Person();
步骤:① 编译器加载 Person.class 文件 ==》
② 查找构造函数 Person() ==》
③ 通过构造函数 Person() 创建对象
反射:
步骤:① 运行期加载 Person.class 文件
Class personClass = Class.forName("com.yuyu.Person");
② 通过 class 对象得到构造函数 Person()
Constructor constructor = personClass.getConstructor();
③ 通过构造函数 Person() 创建对象
Person person = (Person) constructor.newInstance();
@Test
public void test2() throws Throwable{
/** 首先:获得Person的字节码 */
Class personClazz = Class.forName("com.muse.reflect.Person");
/** 其次:通过Class对象,创建构造方法对象 */
Constructor constructor1 = personClazz.getConstructor(); // 初始化无参构造方法
Constructor constructor2 = personClazz.getConstructor(String.class, Integer.class, Byte.class, Boolean.class); // 初始化有参构造方法对象
/** 最后:通过构造方法创建对象 */
// 调用无参数构造方法创建Person对象
Person person1 = (Person) constructor1.newInstance();
person1.setName("muse1");
System.out.println("person1=" + person1);
// 调用有参数构造方法创建Person对象
Person person2 = (Person) constructor2.newInstance("muse2", 10, (byte) 1, true);
System.out.println("person2=" + person2);
}
补充:反射破坏单例
@Test
public void test2() throws Throwable{
/** 补充内容:反射通过私有构造方法创建对象,破坏单例模式 */
Class singletonPersonClazz = SingletonPerson.class;
// Constructor constructor3 = singletonPersonClazz.getConstructor();
// .getDeclaredConstructor()获得的信息更多
Constructor constructor3 = singletonPersonClazz.getDeclaredConstructor();
//获得私有构造方法的使用权限
constructor3.setAccessible(true);
SingletonPerson singletonPerson = (SingletonPerson) constructor3.newInstance();
SingletonPerson singletonPerson1 = SingletonPerson.getInstance();
SingletonPerson singletonPerson2 = SingletonPerson.getInstance();
System.out.println("singletonPerson==singletonPerson1 is " + (singletonPerson == singletonPerson1));//false
System.out.println("singletonPerson==singletonPerson2 is " + (singletonPerson == singletonPerson2));//false
System.out.println("singletonPerson1==singletonPerson2 is " + (singletonPerson1 == singletonPerson2));//true
}
通过反射获取属性 Field
@Test
public void test3() throws Throwable{
// 第一步:获得Class
Class personClazz = Person.class;
// 第二步:获得构造方法
Constructor<Person> constructor = personClazz.getConstructor();
Person person = constructor.newInstance(); // 初始化生成Person对象
// 第三步:通过Class对象,获得Field对象 Person[类]的name属性。
// .getFiled()获取 public 属性
Field nameField = personClazz.getField("name");
// .getDeclareField() 获取非 public属性: private、protect、default 属性,并通过 setAccessible(true) 获得访问权限
Field sexField = personClazz.getDeclaredField("sex");
sexField.setAccessible(true);
//通过反射可以获得私有方法,同样使用 .getDeclareMethod(),并通过 setAccessible(true) 获得访问权限
Method method = personClazz.getDeclaredMethod("privateMethod");
method.setAccessible(true);
// 第四步:操作Field,获得属性值
String name = String.valueOf(nameField.get(person));
System.out.println(name);
//非 public 属性的操作和 public 属性操作一样
System.out.println(sexField.get(person));
//私有方法的操作
System.out.println("private方法:privateMethod()=" + method.invoke(person));
}
反射应用
我们采用反射机制来实现一个工具BeanUtils,可以将一个对象属性名相同的值赋值给另一个对象。(PO、VTO、PO层之间的实体类属性赋值)
public class BeanUtils {
public static void convertor(Object originObj, Object targetObj) throws Throwable {
// 第一步,获得class对象
Class orginClazz = originObj.getClass();
Class targetClazz = targetObj.getClass();
// 第二步,获得Field
Field[] orginFields = orginClazz.getDeclaredFields();
Field[] targetFields = targetClazz.getDeclaredFields();
// 第三步:赋值
for (Field originField : orginFields) {
for (Field targetField : targetFields) {
//属性名相同的赋值给另一个对象
if (originField.getName().equals(targetField.getName())) {
originField.setAccessible(true);
targetField.setAccessible(true);
// 通过对象操作 Field
targetField.set(targetObj, originField.get(originObj));
}
}
}
}
public static void main(String[] args) throws Throwable{
// Service层返回的
Person person = new Person("com/muse", 10, (byte)1, true);
// 需要返回实体对象 person 对象 和 person1 对象只有一个属性名字不同
Person1 person1 = new Person1();
//调用工具类使用反射完成赋值
BeanUtils.convertor(person, person1);
System.out.println("person" + person);
System.out.println("person1" + person1);
}
}
二、泛型
泛型的本质:类型参数化
允许在定义 类、接口、方法 时使用类型形参,当使用时指定具体类型。
所有使用该泛型参数的地方都被统一化,保证类型一致。如果未指定具体类型,默认是Obj ec t类型。集合体系中的所有类都增加了泛型,泛型也主要用在集合。
内容:
① 泛型类;② 泛型方法;③ 泛型类派生出的子类;④ 泛型通配符;⑤类型擦除;⑥桥接方法
① 泛型类
public class ClassGenericity {
public static void main(String[] args) {
/** 创建ObjectTool对象并指定元素类型为String */
ObjectTool<String> stringTool = new ObjectTool<>();
stringTool.setObj("muse");
System.out.println(stringTool.getObj());
/** 创建ObjectTool对象并指定元素类型为Integer */
ObjectTool<Integer> integerTool = new ObjectTool<>();
// integerTool.setObj("muse"); // 编译报错
integerTool.setObj(10);
System.out.println(integerTool.getObj());
}
/**
* 构建可以存储任何类型对象的工具类
*/
static class ObjectTool<T> {
private T obj;
public T getObj() {
return obj;
}
public void setObj(T obj) {
this.obj = obj;
}
}
}
② 泛型方法
泛型类作用在整个类,作用范围较大。泛型方法作用在整个方法,作用范围较小。
public class MethodGenericity{
public static void main(String[] args) {
//创建对象
ObjectTool tool = new ObjectTool();
/** 调用方法,传入的参数是什么类型,T就是什么类型 */
tool.show("hello");
tool.show(12);
tool.show(12.5f);
}
static class ObjectTool {
//定义泛型方法
public <T> void show(T t) {
System.out.println(t);
}
}
}
③ 泛型类派生出的子类
两种情况:① 子类明确泛型类的类型;② 子类不明确泛型类的类型
public class SubclassGenericity {
public static void main(String[] args) {
// 测试第一种情况
Inter<String> i = new InterImpl1();
i.show("hello");
// 编译错误
//Inter<Integer> ii = new InterImpl1();
//ii.show(1);
// 第二种情况测试
Inter<String> iii = new InterImpl2();
iii.show("100");
Inter<Integer> ii = new InterImpl2();
ii.show(1);
}
}
/**
* 把泛型定义在接口上
*/
interface Inter<T> {
void show(T t);
}
/**
* 实现一:子类明确泛型类的类型参数变量
*/
class InterImpl1 implements Inter<String> {
@Override
public void show(String s) {
System.out.println(s);
}
}
/**
* 实现二:子类不明确泛型类的类型参数变量,实现类也要定义出T的类型
*/
class InterImpl2<T> implements Inter<T> {
@Override
public void show(T t) {
System.out.println(t);
}
}
④ 泛型通配符
存在向上向下转换的问题。例如 List<Object> 和 Object 的范围是不一样的,List<Integer> 类型参数不能往 List<Object>里传。
public class TypeWildcard {
private final static List<Integer> INTEGER_LIST = Lists.newArrayList(1, 2, 3, 4);
private final static List<Object> OBJECT_LIST = Lists.newArrayList("a", 1, 'c', 6.0F, 100L, true);
public static void main(String[] args) {
TypeWildcard typeWildcard = new TypeWildcard();
System.out.println("------------test1-------------");
typeWildcard.test1(INTEGER_LIST);
System.out.println("\n------------test2-------------");
// typeWildcard.test2(STRING_LIST);//编译会报错,List<Object>只能传List<Object>类型的参数
typeWildcard.test2(OBJECT_LIST);
}
public void test1(List list) {
for (int i = 0; i < list.size(); i++) {
System.out.print(list.get(i) + " ");
}
}
public void test2(List<Object> list) {
for (int i = 0; i < list.size(); i++) {
System.out.print(list.get(i) + " ");
}
}
}
① 类型名称 < ? > 对象名称
对类型完全不关心,只能读,不能写
public void test4(List<?> list) {
// list.add(list.get(0)); // 编译错误,只能读,不能写
for (int i = 0; i < list.size(); i++) {
System.out.print(list.get(i) + " ");
}
}
② 类型名称 < ? extends 类 > 对象名称
泛型的上限: 只能接收该类型及其子类
上 界 < ? e x t e n d s T > 不 能 往 里 存 , 只 能 往 外 取 。
//上限是知道天花板,所以只能通过天花板类型取数据,但如果存的话不知道是哪个类型的子类,所以不能往里存
public void test5(List<? extends Number> list) {
Number number = list.get(0);
//list.add(number); //编译错误
for (int i = 0; i < list.size(); i++) {
System.out.print(list.get(i) + " ");
}
}
③ 类型名称 < ? super 类 > 对象名称
泛型的下限: 只能接收该类型及其父类型
下 界 < ? s u p e r T > 不 影 响 往 里 存 ,但 往 外 取 只 能 放 在 O b j e c t 对 象 里 。
//下限只知道地板,所以能通过地板类型及其子类存入数据,但往外取数据的时候不知道具体是哪个父类,所以取要用超级父类Object类型
public void test6(List<? super Number> list) {
Number number = (Number) list.get(0);
list.add(number);
// list.add(list.get(0)); // 编译错误,默认取出的是Object类型,但只能存Number及其子类
for (int i = 0; i < list.size(); i++) {
System.out.print(list.get(i) + " ");
}
}
④ 最常用:类型名称 < T > 对象名称 指定类型
public <T> void test3(List<T> list) {
list.add(list.get(0));
for (int i = 0; i < list.size(); i++) {
System.out.print(list.get(i) + " ");
}
}
PECS (Producer Extends Consumer Super)
作为生产者 Producer,使用 extend ,只能读数据提供给消费者(即 extend 服务于读)
作为消费者 Consumer,使用 super , 能写入生产者提供的数据 (即 super 服务于写)
//【读取】
public static void testPECSextends() {
List<Dog> dogs = Lists.newArrayList(new Dog());
// 上限 extend ,只能接收该类型及其子类
List<? extends Animal> animals = dogs;
/**
* animals是一个Animal的子类的List,由于Dog是Animal的子类,因此将dogs赋给animals是合法的,但是编译器会阻止将new Cat()加入animals。
* 因为编译器只知道animals是Animal的某个子类的List,但并不知道究竟是哪个子类,为了类型安全,只好阻止向其中加入任何子类。那么可不可以加入
* new Animal()呢?很遗憾,也不可以。事实上,不能够往一个使用了? extends的数据结构里写入任何的值。
*/
// animals.add(new Cat()); // 编译失败
// animals.add(new Animal()); // 编译失败
// animals.add(new Dog()); // 编译失败
/**
* 由于编译器知道它总是Animal的子类型,但并不知道具体是哪个子类。因此我们总可以从中读取出Animal对象:
*/
Animal animal = animals.get(0);
Object obj = animals.get(0);
// Dog dog = animals.get(0); // 编译失败
}
//【写入】
public static void testPECSsuper() {
List<Animal> animals = Lists.newArrayList();
// 下限 super ,只能接收该类型及其父类型
List<? super Dog> dogs = animals;
/**
* 这里的animals是一个Animal的超类(父类,superclass)的List。同样地,出于对类型安全的考虑,我们可以加入Dog对象或者其任何子类(如WhiteDog)对象,
* 但由于编译器并不知道List的内容究竟是Dog的哪个超类,因此不允许加入特定的任何超类型。
*/
dogs.add(new Dog());
dogs.add(new WhiteDog());
// dogs.add(new Animal()); // 编译失败
// dogs.add(new Cat()); // 编译失败
// dogs.add(new Object()); // 编译失败
/**
* 而当我们读取的时候,编译器在不知道是什么类型的情况下只能返回Object对象,因为Object是任何Java类的最终祖先类。
*/
Object obj = dogs.get(0);
// Dog dog = dogs.get(0); // 编译失败
// Animal animal = dogs.get(0); // 编译失败
}
⑤ 类型擦除
泛型是提供给javac编译器使用的,它用于限定集合的输入类型,让编译器在源代码级别上,即挡住向集合中插入非法数据。但编译器编译完带有泛型的java程序后,生成的class文件中将不再带有泛型信息,以此使程序运行效率不受影响,这个过程称之为 ”类型擦除“。(目的是向下兼容,兼容1.5之前的 jdk 版本)
⑥ 桥接方法
由于类型被擦除了,为了维持多态性,所以编译器就自动生成了桥接方法。
//public interface Animal<T>{
// void eat(T t);
//}
//public class Cat implements Animal<String>(){
// @Override
// public void eat(String s){
// system.out.println("cat eat " + s);
// }
//}
1> 类型擦除
public interface Animal{
void eat(Object t);
}
public class Cat implements Animal(){
public void eat(String s){
system.out.println("cat eat " + s);
}
2> 桥接方法(自动生成)
@Override
public void eat(Object s){
//类型强转
eat((String)s);//类型的限制作用
}
}
三、集合容器
ArrayList源码解析
// 1、实例一个 ArrayList 对象
ArrayList arrayList = new ArrayList();
// 1.2、通过构造函数实例对象
public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
// 【变量源码】
// ArrayList 真正存储数据的是一个 Object 类型的数组,
transient Object[] elementData;
//且默认初始化大小为 0
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
/**
* 【问题1】elementData为什么被transient修饰?
* 答:Transient 关键字的作用是控制变量的序列化,在变量声明前加上该关键字,可以阻
* 止该变量被序列化到文件中,在被反序列化后,transient 变量的值被设为初始值,
* 如 int 型的是 0,对象型的是 null。
* 但是ArrayList在序列化的时候会调用writeObject,直接将size和element写入
* ObjectOutputStream;反序列化时调用readObject,从ObjectInputStream获取
* size和element,再恢复到elementData。
*
* 【问题2】为什么不直接用elementData来序列化,而采用上述的方式来实现序列化呢?
* 答:原因在于elementData是一个缓存数组,它通常会预留一些容量,等容量不足时再扩充
* 容量,那么有些空间可能就没有实际存储元素,采用上述的方式来实现序列化时,就可以保
* 证只序列化实际存储的那些元素,而不是整个数组,从而节省空间和时间。
*/
// 2、对 ArrayList 对象进行操作
arrayList.add("a");
// 2.1 add 新增元素操作
// eg1:第一次新增元素e="a1"
public boolean add(E e) {
/** 用于查看是否有存储空间:确定是否需要扩容,如果需要,则进行扩容操作*/
ensureCapacityInternal(size + 1);
// eg1:size=0,elementData[0]="a1",然后size++自增为1
elementData[size++] = e;
return true;
}
// 【变量源码】
// size 默认为 0 ,需要进入到 ensureCapacityInternal() 函数查看存储空间
private int size;
// 2.2 ensureCapacityInternal() 确认存储空间
// eg1:第一次新增元素,所以size=0,则:minCapacity=size+1=1
private void ensureCapacityInternal(int minCapacity) {
// eg1:第一次新增元素,calculateCapacity方法返回值为DEFAULT_CAPACITY=10
ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}
// elementData 当前 默认为 0
// minCapacity 当前为 size + 1 = 1
// 2.2.1 进入到 calculateCapacity() 计算容量
// eg1:第一次新增元素,elementData={} minCapacity=1
private static int calculateCapacity(Object[] elementData, int minCapacity) {
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
// eg1:满足if判断,DEFAULT_CAPACITY=10
return Math.max(DEFAULT_CAPACITY, minCapacity);
}
return minCapacity;
}
// 【变量源码】
// 判断 elementData 是否为空数组
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
// 默认第一次操作容量为 10
private static final int DEFAULT_CAPACITY = 10;
// 2.2.2 进入到 ensureExplicitCapacity() 查看是否需要扩容
// eg1:第一次新增元素,minCapacity=10
private void ensureExplicitCapacity(int minCapacity) {
// eg1: modCount++后,modCount=1
modCount++;
/** 如果所需的最小容量大于elementData数组的容量,则进行扩容操作 */
if (minCapacity - elementData.length > 0) { // eg1:10-0=10,满足扩容需求
// eg1:minCapacity=10
grow(minCapacity);
}
}
// 2.3 进入到 grow() 进行扩容
// eg1:第一次新增元素,minCapacity=10,即:需要将elementData的0长度扩容为10长度。
private void grow(int minCapacity) {
/** 原有数组elementData的长度*/
int oldCapacity = elementData.length; // eg1:oldCapacity=0
/** 新增 oldCapacity 的一半整数长度作为 newCapacity 的额外增长长度 */
int newCapacity = oldCapacity + (oldCapacity >> 1);
// eg1:newCapacity=0+(0>>1)=0
/**
* 新的长度 newCapacity 依然无法满足需要的最小扩容量 minCapacity,则新的扩容
* 长度为 minCapacity
*/
if (newCapacity - minCapacity < 0) {
// eg1:newCapacity=10
newCapacity = minCapacity;
}
/** 新的扩容长度 newCapacity 超出了最大的数组长度 MAX_ARRAY_SIZE */
if (newCapacity - MAX_ARRAY_SIZE > 0) {
newCapacity = hugeCapacity(minCapacity);
}
/** 扩展数组长度为newCapacity,并且将旧数组中的元素赋值到新的数组中 */
// eg1:newCapacity=10, 扩容 elementData的length=10
elementData = Arrays.copyOf(elementData, newCapacity);
}
// 【变量源码】
// MAX_ARRAY_SIZE=2147483639=01111111 11111111 11111111 11110111
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
// 2.4 判断是否过大造成内存溢出
private static int hugeCapacity(int minCapacity) {
if (minCapacity < 0) { // overflow
throw new OutOfMemoryError();
}
return (minCapacity > MAX_ARRAY_SIZE) ? Integer.MAX_VALUE : MAX_ARRAY_SIZE;
}
// 【变量源码】
// 01111111 11111111 11111111 11111111 = 2147483647
@Native public static final int MAX_VALUE = 0x7fffffff;
// 3、空间确认完成后回到 add() 进行元素新增
elementData[size++] = e;
LinkedList源码解析
// 1、实例一个 LinkedList 对象
LinkedList linkedList = new LinkedList();
// 1.2、通过构造函数实例对象
public LinkedList() {
}
// 2、对 LinkedList 对象进行操作
linkedList.add("a");
// 2.1 add 新增元素操作 得到一个真假的新增结果
// eg1: e="a1"
public boolean add(E e) {
linkLast(e);
return true;
}
// 【Node 节点源码】
private static class Node<E> {
E item; // 结点元素
Node<E> next; // 后置结点指针
Node<E> prev; // 前置结点指针
Node(Node<E> prev, E element, Node<E> next) {
this.item = element;
this.next = next;
this.prev = prev;
}
}
// 2.2 进入 linkLast(e) 函数完成添加操作
//将新添加的元素 e 作为链表的最后一个元素, 并维护进去
// eg1: e="a1"
void linkLast(E e) {
//首先得到链表最后的节点,首次初始化默认为空
final Node<E> l = last;
// eg1: newNode null<--"a1"-->null
/** 创建一个e的Node节点,前置指向原last节点,后置指向null */
final Node<E> newNode = new Node<>(l, e, null);
/** 将newNode节点赋值为last节点 */
last = newNode;
// eg1: l=null
if (l == null) {
/** 如果是第一个添加的元素,则first指针指向该结点*/
first = newNode; // eg1: first指向newNode
} else {
/** 如果不是第一个添加进来的元素,则更新l的后置结点指向新添加的元素结点newNode*/
l.next = newNode;
}
size++;
modCount++;
}
// 【变量源码】
// 默认最后一个节点为空(-->null)【last 起指针作用】
transient Node<E> last;
// 默认首个节点为空(null<--)【first 起指针作用】
transient Node<E> first;
// 默认大小为 0
transient int size = 0;
// 3.1 remove 删除元素操作 操作返回被删除节点的值
// eg1:链表中保存了{"a1","a2","a3","a4"},删除第一个元素,即:index=0
public E remove(int index) {
/**
* 校验传入的参数index是否超出了链表的最大下标且下标不为负数,
* 如果超出,则抛出:IndexOutOfBoundsException异常
*/
checkElementIndex(index);
// eg1:node(index)返回需要删除的结点,即:"a1"
return unlink(node(index)); /** 断开待删除结点的链接 */
}
// 3.2 先 checkElementIndex(index) 检验传入参数是否合法(节点是否存在)
private void checkElementIndex(int index) {
// index >= 0 && index < size 是否超出了链表的最大下标且下标不为负数
if (!isElementIndex(index)) {
// 如果超出,则抛出:IndexOutOfBoundsException异常
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}
}
// 是否超出了链表的最大下标且下标不为负数
private boolean isElementIndex(int index) {
return index >= 0 && index < size;
}
// 3.3 然后拿到节点 node(index) 再将此节点的前后节点指向null unlink(node)
// 链表二分法查找节点
Node<E> node(int index) {
/** 如果需要获取的index小于总长度size的一半,则从头部开始向后遍历查找 */
if (index < (size >> 1)) {
Node<E> x = first;
for (int i = 0; i < index; i++) {
x = x.next; // 从first结点向后next查找,直到index下标node,返回node
}
return x;
} else { /** 从尾部开始向前遍历查找 */
Node<E> x = last;
for (int i = size - 1; i > index; i--) {
x = x.prev; // 从last结点向前prev查找,直到index下标node,返回node
}
return x;
}
}
// 3.4 将获取到的节点前后指针指向 null
// eg1: x null<--"a1"-->"a2"
E unlink(Node<E> x) {
final E element = x.item;
final Node<E> next = x.next;
final Node<E> prev = x.prev;
/** x.prev为null,表示x结点为第一个元素*/
if (prev == null) {
first = next; // 更新first头指针为x结点的后置结点
} else {
prev.next = next; // 将x的前置结点与x的后置结点相连接
x.prev = null; // 断开x的前置指针
}
/** x.next为null,表示x结点为最后一个元素*/
if (next == null) {
last = prev;
} else {
next.prev = prev; // 将x的后置结点与x的前置结点相连接
x.next = null; // 断开x的后置指针
}
x.item = null;
size--;
modCount++;
return element;
}