前言
1、反射
使得java可以在程序运行动态期间创建类的实例,这样也扩展了后来的动态代理(基于反射实现,还有一种是基于cglib实现),动态代理和生活中的代理一个思想,红牛,他有很多委托的代理厂商去生产(大学寒暑假工代理,房产中介),肯定在每个代理中,在原有最基本的功能上,又会有一些不一样的特殊功能(价格优惠,优惠券…),每有一个新的代理,那么就会有一个新的功能(如果没有,那代理就没有太大的意义了)。而我们java程序中也是如此,模拟我们现实生活场景,在不使用反射的情况下,代码是我们提前写好了的,一旦程序运行,想要增加一个代理类,那么必须修改代码,项目重启,而且代理类可能有很多个,手写也会很麻烦。
2、类加载:类加载时静态变量赋值顺序和静态代码块的顺序(代码顺序),静态代码块不执行的情况(子类调用父类没有重写的静态方法,调用常量)
3、泛型:定义与使用,继承中的使用,通配符,泛型与反射
一、类加载
java程序运行时,首先会加载程序中所有的class文件,这个过程就是类加载,具体的流程在另外一篇中有赘述:
浅谈java类加载机制
加载类的工具是类加载:核心类加载器(rt.jar),扩展类加载器(ext.jar),系统类加载器,
加载过程:加载(创建class对象),验证(class文件中格式,),准备(分配内存),解析(符号引用转为直接引用),初始化(静态变量初始化)
类加载机制:双亲委派机制(自上而下加载,防止修改jdk中核心类)
二、反射
1、理解
1)反射:反射机制使得java可以在程序运行动态期间创建类的实例,这样也扩展了后来的动态代理(基于反射实现,还有一种是基于cglib实现),通过jdk提供的Class类,可以操控内存的字节码对象(对象是唯一的,是在程序启动时类加载过程中产生的)
2)动态代理:动态代理和生活中的代理一个思想,红牛,他有很多委托的代理厂商去生产(大学寒暑假公代理,房产中介),肯定在每个代理中,在原有最基本的功能上,又会有一些不一样的特殊功能(价格优惠,优惠券…),每有一个新的代理,那么就会有一个新的功能(如果没有,那代理就没有太大的意义了)。而我们java程序中也是如此,模拟我们现实生活场景,在不使用反射的情况下,代码是我们提前写好了的,一旦程序运行,想要增加一个代理类,那么必须修改代码,项目重启,而且代理类可能有很多个,手写也会很麻烦。
2、获取Class对象的三种方式以及创建对象
1)获取Class对象
public class GetClass {
public static void main(String[] args) {
Class classz = Animal.class;//和数据的length属性类似
Class classz2 = new Animal().getClass();//对象的调用方法
Class calssz3 = Class.forName("com.test_reflect.test_method.Animal");//class的静态方法,需要类型的全限定名称
}
}
2)创建对象
public class test {
public static void main(String [] args){
try {
Class animalClass = Class.forName("com.test_reflect.test_Constructor.Animal");
Constructor constructor = animalClass.getDeclaredConstructor(String.class);//获取指定构造方法
Animal animal = (Animal) constructor.newInstance("牛马");//利用Constructor类调用指定的有参构造方法创建对象
animalClass.newInstance();//弃用,利用字节码对象利用无参数构造方法创建对象
System.out.println(animal.getName());
} catch (ClassNotFoundException e) {
System.out.println(e.getMessage());
} catch (IllegalAccessException e) {
System.out.println(e.getMessage());
} catch (InstantiationException e) {
System.out.println(e.getMessage());
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
}
class Animal{
private String name;
public Animal(){
this("此动物名字待定");
System.out.println("无参构造方法执行了!");
}
public Animal(String name){
System.out.println("有参构造方法执行了!");
this.name=name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
3、常用方法和相关类
1)Field
①获取属性修饰符,类型,变量名
/**
* 变量:修饰符,变量类型,变量名
*/
public class test {
public static void main(String [] args){
Class userClass=null;
try {
userClass = Class.forName("com.test_reflect.test_fields.User");
} catch (ClassNotFoundException e) {
System.out.println(e.getMessage());
}
if(userClass!=null){
Field fields [] = userClass.getDeclaredFields();//获取所有的变量
try {
Object userObj = userClass.newInstance();//调用无参构造方法,创建实例
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
StringBuffer str = new StringBuffer();
for(Field f:fields){
str.append(Modifier.toString(f.getModifiers())+" ");//获取修饰符
str.append(f.getType().getSimpleName()+" ");//获取变量类型,可能是
str.append(f.getName()+";");//获取变量名
str.append("\n");
}
System.out.println(str);
}
}
}
②操作属性,打破修饰规则,属性值得获取与修改
public class test_set_get {
public static void main(String[] args) {
try {
Class userClass = Class.forName("com.test_reflect.test_fields.User");
User user = new User();
user.setUsername("ly");
Object userObj = userClass.newInstance();//方法已过时;
Field field = userClass.getDeclaredField("username");
field.setAccessible(true);//打破规则,
field.set(userObj , "liyang");//因为是private修饰,必须打破权限
System.out.println(field.get(userObj));//获取属性,
} catch (ClassNotFoundException e) {
System.out.println(e.getMessage());
} catch (IllegalAccessException e) {
System.out.println(e.getMessage());
} catch (InstantiationException e) {
e.printStackTrace();
} catch (NoSuchFieldException e) {
System.out.println("没有找到属性");
}
}
}
2)Method
①获取方法信息
在继承关系,真正意义上的类只是继承了父类特征,调用的是父类方法,并不是自己拥有,所谓重写感觉就是在说自己拥有了父类的方法。(变量也是这个意思),
可变长度参数(就是数组,只能在形参最后,所以也只能有一个)获取的修饰符含有transient,数组不会,
public class test {
public static void main(String[] args) {
try {
Class dogClass = Class.forName("com.test_reflect.test_method.Animal");
Method [] methods=dogClass.getDeclaredMethods();//获取所有方法,不包括静态代码块
StringBuffer str = new StringBuffer();
dogClass.getDeclaredMethod("speak",String.class,int.class);//根据形参和方法名获取唯一的方法
for(Method method:methods){
str.append(Modifier.toString(method.getModifiers())+" ");//获取方法的修饰符
str.append(method.getReturnType()+" ");//获取返回值类型
str.append(method.getName()+"(");//获取方法名
Parameter[] parameters = method.getParameters();//获取形参类
for(Parameter para:parameters){
str.append(para.getType().getSimpleName()+",");//获取形参的类型简单名字
}
if(parameters.length>0)
str.deleteCharAt(str.length()-1);
str.append("){}");
str.append("\n");
}
System.out.println(str);
} catch (ClassNotFoundException e) {
System.out.println("没有加载到指定类");
} catch (NoSuchMethodException e) {
System.out.println("没有找到这个方法");
}
}
}
②方法调用
方法调用的返回值为Object,如果返回值类型为void,那么Object为null
public class test_method_doSome {
public static void main(String[] args) {
try {
Class catClass = Class.forName("com.test_reflect.test_method.Animal");
Object catObj = catClass.newInstance();
Method method = catClass.getDeclaredMethod("speak",String.class,int.class);//获取指定method放酦
method.invoke(catObj,"fds",432);//调用方法,方法对象,形参
} catch (ClassNotFoundException e) {
System.out.println("没有找到指定的类");
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
System.out.println("没有找到这个方法");
} catch (InvocationTargetException e) {
System.out.println("执行方法时出错");
}
}
}
3)Interface,Annotation,SuperClass
public class getSome {
public static void main(String[] args) {
Class son1Class = Son1.class;
Class superClass = son1Class.getSuperclass();//获取父类Class对象
Class [] interfaces = son1Class.getInterfaces();//获取实现的接口Class对象
System.out.println("superClassName:"+superClass.getSimpleName()+"/"+superClass.getName());
for (Class anInterface : interfaces) {
System.out.println("implementsInterface:"+anInterface.getSimpleName());
}
}
}
4、功能实现(反射与泛型)
1)读取excel信息
需要依赖
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml</artifactId>
<version>3.15</version>
</dependency>
处理工具类
package org.example.HandlerUtils;
import org.apache.poi.hssf.usermodel.HSSFDateUtil;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.usermodel.XSSFRow;
import org.example.hlxyh.Column;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.*;
import static java.sql.Types.NUMERIC;
import static org.apache.poi.ss.usermodel.DataValidationConstraint.ValidationType.FORMULA;
import static org.apache.xmlbeans.impl.piccolo.xml.Piccolo.STRING;
/**
* 转储的实体类需要加上@Column注解,没有注解标明的实体类中的实体类是不会存储的不会存储
* sheet表格格式:第0行表示列名,第0列没有任何意义
* bean对象属性赋值通过set方法(set+属性名首字母大写)
*/
public class HandlerUtil {
public static <T> List<T> doSome(Class<T> classz, Sheet sheet){
T t = null;
Map<String,String> map = new HashMap<>();//存储列名和实体类的映射关系;
List<T> list = new ArrayList<>();
try {
int rows = sheet.getPhysicalNumberOfRows();//获取总行数
int cols = sheet.getRow(0).getPhysicalNumberOfCells();//获取第一行的列数。
if((map = mapAdapter(classz))==null) {
System.out.println("映射错误");
return null;
}
for(int i = 1; i <rows;i++) {//遍历每一行的内容,
Row row = sheet.getRow(i);
t = classz.newInstance();
for (int j = 1; j < cols; j++)//遍历每一列
{
String data = row.getCell(j).toString();//获取属性
String field = map.get(sheet.getRow(0).getCell(j).toString());//获取赋值的属性名
setData(classz,t,field,data);
}
list.add(t);
//存储实体类
}
} catch (InstantiationException e) {
System.out.println("newInstance error");
} catch (IllegalAccessException e) {
System.out.println("newInstance error");
}
return list;
}
public static <T> void setData(Class<T> classz,T t,String field,String data){
if(field==null) return;
Method [] methods = classz.getDeclaredMethods();
//String field = map.get(col)!=null?map.get(col):col;
char chars [] = field.toCharArray();
chars[0]-=32;
String methodName = "set"+new String(chars);
try {
Method method = classz.getMethod(methodName,String.class);
method.invoke(t,data);
} catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
System.out.println("获取属性的set方法失败,获取调用set方法失败");
}
}
public static Map mapAdapter(Class classz){
HashMap<String,String> map = new HashMap();
Field [] fields = classz.getDeclaredFields();
for (Field field : fields) {
Column col = field.getAnnotation(Column.class);
if(null!=col) map.put(col.value(),field.getName());//有注解的属性名
//else map.put(field.getName(),field.getName());//属性名和列名相同
}
return map;
}
}
@Column注解
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)//可存在于程序运行期间获取
public @interface Column {
public String value();
}
实体类可自行测试
public class Bean {
@Column("姓名")
private String name;
@Column("手机号")
private String tel;
@Column("所在学院")
private String job;
@Column("入学年份")
private String time;
@Column("现居城市")
private String address;
}
2)(数据库信息读取,Http请求参数)数据库信息读取,Http请求参数)参数信息封装
public class BeanUtil {
public static <T> T getBean(Class<T> classz, Map<String,Object> map){
T obj = null;
try {
obj = classz.newInstance();//实例对象
Iterator<String> it = map.keySet().iterator();
while(it.hasNext()) {
String key = it.next();
Object value = map.get(key);
Field field = classz.getDeclaredField(key);//获取map集合中的key,也就是实体的属性名
field.setAccessible(true);//打破private修饰规则
field.set(obj,value);
}
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (NoSuchFieldException e) {
e.printStackTrace();
}
return obj;
}
}
5、动态代理(目标类必须实现接口)
Proxy(反射)的newProxyInstanceof(Class classLoader,Class [] interfaces,InovationHandler handler)方法,可以创建一个指定类(接口,mybatis的dao层就是这样)的代理类,也就是通过class对象获取到它实现的接口和类加载,再根据我们自己实现的InvocationHandler接口的invoke方法,创建代理对象(是接口的实现类)),当代理对象执行方法时,就会触发invoke方法
2)目标类以及目标类实现的接口
①目标类UsbKingFactory
public class UsbKingFactory implements UsbSale {
@Override
public float sale(int amount) {
System.out.println("目标类的方法执行了,");
return 85f;
}
@Override
public void print() {
System.out.println("您成功的买了U盘");
}
}
②接口UsbSale
public interface UsbSale {
public float sale(int amount);
public void print();
}
2)InvocationHandler实现类
public class MyInvocationHandler implements InvocationHandler {
private Object target;
public MyInvocationHandler(Object target){
this.target=target;//在实例化时,获取到目标类,这样就可以在代理时获取到目标的方法
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
float price = -1;
Object res = null;
res = method.invoke(target,args);//首先调用目标类的方法,保证基本方法执行
if(null!=res){//设置自己代理对象的特殊效果
price = (float)res+25;
}
return price;
}
}
三、泛型
1、理解
①在JDK1.5之前,集合中所能存储的类是不确定的,可以存储任意类型(类似图书馆图书分类,垃圾箱分类一个道理),一个集合中的内容太杂。为了解决这一问题,泛型油然而生
②泛型的声明和作用域:
就是可以在声明类,接口时通过一个标识符表示或者是在方法中定义的一种数据类型,用该标识代表的这种数据类型,可以用在这个数据类型所能用的任何地方(定义变量,方法返回值,不可用于静态变量和静态方法的返回值,静态方法不可用类的泛型,catch异常捕捉不可用),在我们变量声明和方法的形参类型中指定标识对应的数据类型,如果没有指定,默认是Object类型
③子类可以继承父类
class Father<T1, T2> {
}
// 子类不保留父类的泛型
// 1)没有类型 擦除
class Son<A, B> extends Father{//等价于class Son extends Father<Object,Object>{
}
// 2)具体类型
class Son2<A, B> extends Father<Integer, String> {
}
// 子类保留父类的泛型
// 1)全部保留
class Son3<T1, T2, A, B> extends Father<T1, T2> {
}
// 2)部分保留
class Son4<T2, A, B> extends Father<Integer, T2> {
④通配符?,表示无法确定的类型,也就是可以为任意类型(和普通声明一样,但是无法存入信息,但是可以通过变量直接赋值),但是无法直接存入指定类型的元素,但是可以取出类型,因为都属于Object子类
public class TestOther {
public static void main(String[] args) {
List list = new ArrayList();
List<?> list2 = list;//==>List<?> list3 = list;
}
}
约束:List<? extends 类/接口> list , List<? super 类/接口> list,
⑤优点:在集合中取出元素不需要强转,并且可以自动检测元素内容是否符合要求。
2、泛型的使用
1)集合
不使用泛型:无法确定一个指定集合中的元素类型,数据需要强转
public class Test {
public static void main(String[] args) {
List list = new ArrayList();
list.add(123);
list.add(23);
list.add(12);
list.add(13);
list.add("score");//存入了错误的数据,
for(Object obj : list){
Integer score = (Integer) obj;//取出数据是需要强转,如果不是int类型,那么会出错
System.out.println(score);
}
}
}
②使用泛型:类型不符合,编译时出错
/**
* 一个存储成绩的集合
*/
public class Test {
public static void main(String[] args) {
List<Integer> list = new ArrayList();
list.add(123);
list.add(23);
list.add(12);
list.add(13);
//list.add("score");//存入了错误的数据,编译时出错
for(Integer score : list){
System.out.println(score);
}
}
2)自定义泛型
①类/接口
public class MyClass<T> {
private T t;
public static void main(String[] args) {
MyClass<Date> myClass = new MyClass();
myClass.t=new Date();
System.out.println(myClass.t);
}
}
②方法
public <R> R doSome(R r){return r;}//只在方法内部有效