jdk1.5新特性
jdk1.5新特性
先一睹为快
1. 自动封箱、自动拆箱
2. 泛型
3. 增强for循环
4. 静态导入
5. 可变参数
6. 枚举
7. 注解
8. 反射
9. 动态代理
10. 内省
自动封箱拆箱
-
包装类中
-
自动封箱
Integer i=5;//基本类型自动成为包装类
-
自动拆箱
if(i==5){...}//包装类自动成为基本类型
-
自动封箱
泛型
1. 类中的泛型
class Test<T>{
public List<T> getPersons(){
...
}
}
2. 方法中的泛型
public <R> R getResource(R r){
...
}
3. 泛型的上下限
1. 上限
<? extends 类名或接口名>
2. 下限
<? super 类名或接口名>
- 参数化类型--ParameterizedType--Jdk1.5
- 早期的集合没有泛型:在底层全部标记为Object类型,没有给定类型时,可以放任何类型的数据;取的时候需判断instanceof;
- 早期的泛型List list=new ArrayList(); 只是建议的作用,没有啥太大的作用。
- 后续版本:前后都加泛型List list=new ArrayList();
- 泛型的擦除:发生在编译期,把泛型替换为具体的类型
- 自定义泛型
class Demo<T>{
T t;
private void set(T t){
this.t=t;
}
}
class Demo<T,E>{
T t;
private void set(T t,E e){
this.t=t;
this.e=e;
}
}
函数中的泛型
publiv <T,E> T method1(E e){
return (T)e;
}
- 可以为类和方法都可以用泛型来声明
- 不允许声明泛型变量(错误举例:private v;)
- 泛型的命名:符合标识符的命名,习惯上用一个大写字母来表示
- 泛型以用<>来声明的
- T type
- E element
- R result
- V value
- K key
泛型不兼容子类:泛型传入子类出错
泛型的继承:通配符?
无论类还是接口都用extends关键字;限定了泛型的上限(不能直接添加元素--上造型对象反向--doubl和Integet时错误)
public void print(List<? extends Number> list){
}
super关键字限定下限(能直接添加元素--上造型对象正向,任何父类都可以添加子类对象)
public void print(List<? super String> list){
}
上下限不能同时出现!!
增强for循环
1. 形式:for(类型 变量名:数组或集合){//具体操作}
2. 举例:
int[] arr={1,2,3};
for(int i : arr){
System.out.print(i+"\t");
}
静态导入
1. 形式:import static 包名.具体方法名;
2. 举例:
import static java.util.Arrays.sort;
import static java.util.Arrays.toString;
public class Test{
public static void main(String[] args){
int[] arr={1,4,3,2};
arr=sort(arr);
System.out.println(toString(arr));
}
}
可变参数
1. 形式:类型... 变量名
2. 位置:参数列表的最后一个(类型1 参数名1,类型2 参数名2,类型3... 变量名3)
3. 特性:本质是一个数组;不限定参数个数(0-n);也可以传入数组;
4. 举例:
public int sum(int... arr){
int num=0;
for(int temp:arr){
num+=temp;
}
return num;
}
枚举
1. 适用场景:可以固定并能一一列举的场景
2. 形式
enum Season{
Spring,Summer,Autumn,Winter;
}
等价为:
public static final Season Spring = new Season();
public static final Season Sunmmer = new Season();
public static final Season Autumn = new Season();
public static final Season Winter = new Season();
3. 本质为类
4. 枚举定义的枚举常量位置:枚举类的首行
5. 其他性质--允许类的操作
1. 允许定义一切方法和属性
2. 构造函数--只能是私有的
enum Season{
Spring(1),Summer(4),Autumn(7),Winter(11);//必须首行
int num=-1;
public Season(int num){
this.num=num;
}
}
3. 可以写抽象方法
enum Season{
Spring(1){
public void getTime(){
System.out.println("3,4,5");
}
},Summer(4){
public void getTime(){
System.out.println("6,7,8");
}
},Autumn(7){
public void getTime(){
System.out.println("9,10 ,11");
}
},Winter(11){
public void getTime(){
System.out.println("12,1,2");
}
};//必须首行
int num=-1;
public Season(int num){
this.num=num;
}
//抽象方法
public abstract void getTime();
}
4. enum的*父类:java.lang.Enum
5. switch(参数)-case结构:参数可为byte/short/char/int,JDK1.5开始支持enum,JDK1.7开始支持String
注解
-
什么是注解
- Annotation(注解)就是Java提供了一种元程序中的元素关联任何信息和着任何元数据(metadata)的途径和方法。Annotion(注解)是一个接口,程序可以通过反射来获取指定程序元素的Annotion对象,然后通过Annotion对象来获取注解里面的元数据。
- Annotation(注解)是JDK5.0及以后版本引入的。它可以用于创建文档,跟踪代码中的依赖性,甚至执行基本编译时检查。从某些方面看,annotation就像修饰符一样被使用,并应用于包、类 型、构造方法、方法、成员变量、参数、本地变量的声明中。这些信息被存储在Annotation的“name=value”结构对中。
- Annotation的成员在Annotation类型中以无参数的方法的形式被声明。其方法名和返回值定义了该成员的名字和类型。在此有一个特定的默认语法:允许声明任何Annotation成员的默认值:一个Annotation可以将name=value对作为没有定义默认值的Annotation成员的值,当然也可以使用name=value对来覆盖其它成员默认值。这一点有些近似类的继承特性,父类的构造函数可以作为子类的默认构造函数,但是也可以被子类覆盖。
- Annotation能被用来为某个程序元素(类、方法、成员变量等)关联任何的信息。需要注意的是,这里存在着一个基本的规则:Annotation不能影响程序代码的执行,无论增加、删除 Annotation,代码都始终如一的执行。另外,尽管一些annotation通过java的反射api方法在运行时被访问,而java语言解释器在工作时忽略了这些annotation。正是由于java虚拟机忽略了Annotation,导致了annotation类型在代码中是“不起作用”的; 只有通过某种配套的工具才会对annotation类型中的信息进行访问和处理。本文中将涵盖标准的Annotation和meta-annotation类型,陪伴这些annotation类型的工具是java编译器(当然要以某种特殊的方式处理它们)
-
注解分类
-
根据注解参数的个数,我们可以将注解分为三类:
- 1.标记注解:一个没有成员定义的Annotation类型被称为标记注解。这种Annotation类型仅使用自身的存在与否来为我们提供信息。比如后面的系统注解@Override;、
- 2.单值注解
- 3.完整注解
-
根据注解使用方法和用途,我们可以将Annotation分为三类:
- 1.JDK内置系统注解
- 2.元注解
- 3.自定义注解
-
根据注解参数的个数,我们可以将注解分为三类:
-
系统内置标准注解
-
@Override:用于修饰此方法覆盖了父类的方法;
public class Fruit { public void displayName(){ System.out.println("水果的名字是:*****"); } } class Orange extends Fruit { @Override public void displayName(){ System.out.println("水果的名字是:桔子"); } }
-
@Deprecated:用于修饰已经过时的方法;
class AppleService { public void displayName(){ System.out.println("水果的名字是:苹果"); } /** * @deprecated 该方法已经过期,不推荐使用 */ @Deprecated public void showTaste(){ System.out.println("水果的苹果的口感是:脆甜"); } public void showTaste(int typeId){ if(typeId==1){ System.out.println("水果的苹果的口感是:酸涩"); } else if(typeId==2){ System.out.println("水果的苹果的口感是:绵甜"); } else{ System.out.println("水果的苹果的口感是:脆甜"); } } }
-
@SuppressWarnnings:用于通知java编译器禁止特定的编译警告。
public class FruitService { @SuppressWarnings(value={ "rawtypes", "unchecked" }) public static List<Fruit> getFruitList(){ List<Fruit> fruitList=new ArrayList(); return fruitList; } @SuppressWarnings({ "rawtypes", "unchecked" }) public static List<Fruit> getFruit(){ List<Fruit> fruitList=new ArrayList(); return fruitList; } @SuppressWarnings("unused") public static void main(String[] args){ List<String> strList=new ArrayList<String>(); } }
-
-
自定义注解
-
元注解
- 元注解的作用就是负责注解其他注解。Java5.0定义了4个标准的meta-annotation类型,它们被用来提供对其它 annotation类型作说明。Java5.0定义的元注解:
-
@Target(ElementType.TYPE) public @interface Table { /** * 数据表名称注解,默认值为类名称 * @return */ public String tableName() default "className"; } @Target(ElementType.FIELD) public @interface NoDBColumn { }
-
[email protected]
@Target(ElementType.FIELD) @Retention(RetentionPolicy.RUNTIME) public @interface Column { public String name() default "fieldName"; public String setFuncName() default "setField"; public String getFuncName() default "getField"; public boolean defaultDBValue() default false; }
-
[email protected]
@Target(ElementType.FIELD) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface Column { public String name() default "fieldName"; public String setFuncName() default "setField"; public String getFuncName() default "getField"; public boolean defaultDBValue() default false; }
-
[email protected]
/** * * @author peida * */ @Inherited public @interface Greeting { public enum FontColor{ BULE,RED,GREEN}; String name(); FontColor fontColor() default FontColor.GREEN; }
-
自定义
- 使用@interface自定义注解时,自动继承了java.lang.annotation.Annotation接口,由编译程序自动完成其他细节。在定义注解时,不能继承其他的注解或接口。@interface用来声明一个注解,其中的每一个方法实际上是声明了一个配置参数。方法的名称就是参数的名称,返回值类型就是参数的类型(返回值类型只能是基本类型、Class、String、enum)。可以通过default来声明参数的默认值。
-
定义注解格式:
- public @interface 注解名 {定义体}
-
注解参数的可支持数据类型:
- 1.所有基本数据类型(int,float,boolean,byte,double,char,long,short)
- 2.String类型
- 3.Class类型
- 4.enum类型
- 5.Annotation类型
- 6.以上所有类型的数组
-
Annotation类型里面的参数该怎么设定:
- 第一,只能用public或默认(default)这两个访问权修饰.例如,String value();这里把方法设为defaul默认类型;
-
第二,参数成员只能用基本类型
byte,short,char,int,long,float,double,boolean 八种基本数据类型和 String,Enum,Class,annotations等数据类型, 以及这一些类型的数组.例如,String value();这里的参数成员就为String;
- 第三,如果只有一个参数成员,最好把参数名称设为"value",后加小括号.例:下面的例子FruitName注解就只有一个参数成员。
-
简单的自定义注解和使用注解实例:
package annotation; import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** * 水果名称注解 * @author peida * */ @Target(ElementType.FIELD) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface FruitName { String value() default ""; }
package annotation; import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** * 水果颜色注解 * @author peida * */ @Target(ElementType.FIELD) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface FruitColor { /** * 颜色枚举 * @author peida * */ public enum Color{ BULE,RED,GREEN}; /** * 颜色属性 * @return */ Color fruitColor() default Color.GREEN; }
package annotation; import annotation.FruitColor.Color; public class Apple { @FruitName("Apple") private String appleName; @FruitColor(fruitColor=Color.RED) private String appleColor; public void setAppleColor(String appleColor) { this.appleColor = appleColor; } public String getAppleColor() { return appleColor; } public void setAppleName(String appleName) { this.appleName = appleName; } public String getAppleName() { return appleName; } public void displayName(){ System.out.println("水果的名字是:苹果"); } }
package annotation; import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** * 水果供应者注解 * @author peida * */ @Target(ElementType.FIELD) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface FruitProvider { /** * 供应商编号 * @return */ public int id() default -1; /** * 供应商名称 * @return */ public String name() default ""; /** * 供应商地址 * @return */ public String address() default ""; }
/***********注解使用***************/ public class Apple { @FruitName("Apple") private String appleName; @FruitColor(fruitColor=Color.RED) private String appleColor; @FruitProvider(id=1,name="陕西红富士集团",address="陕西省西安市延安路89号红富士大厦") private String appleProvider; public void setAppleColor(String appleColor) { this.appleColor = appleColor; } public String getAppleColor() { return appleColor; } public void setAppleName(String appleName) { this.appleName = appleName; } public String getAppleName() { return appleName; } public void setAppleProvider(String appleProvider) { this.appleProvider = appleProvider; } public String getAppleProvider() { return appleProvider; } public void displayName(){ System.out.println("水果的名字是:苹果"); } }
/***********注解处理器***************/ public class FruitInfoUtil { public static void getFruitInfo(Class<?> clazz){ String strFruitName=" 水果名称:"; String strFruitColor=" 水果颜色:"; String strFruitProvicer="供应商信息:"; Field[] fields = clazz.getDeclaredFields(); for(Field field :fields){ if(field.isAnnotationPresent(FruitName.class)){ FruitName fruitName = (FruitName) field.getAnnotation(FruitName.class); strFruitName=strFruitName+fruitName.value(); System.out.println(strFruitName); } else if(field.isAnnotationPresent(FruitColor.class)){ FruitColor fruitColor= (FruitColor) field.getAnnotation(FruitColor.class); strFruitColor=strFruitColor+fruitColor.fruitColor().toString(); System.out.println(strFruitColor); } else if(field.isAnnotationPresent(FruitProvider.class)){ FruitProvider fruitProvider= (FruitProvider) field.getAnnotation(FruitProvider.class); strFruitProvicer=" 供应商编号:"+fruitProvider.id()+" 供应商名称:"+fruitProvider.name()+" 供应商地址:"+fruitProvider.address(); System.out.println(strFruitProvicer); } } } }
-
反射
类的加载
-
使用类时,类未被加载到内存中--加载,连接,初始化
- 加载:将class文件读入内存,并为之创建一个class对象,任何类被使用时系统都会建立一个class对象
-
连接
- 验证 是否有正确的内部结构,并和其他类协调一致
- 准备 负责为类的静态成员分配内存,并设置默认初始化值
- 解析 将类的二进制数据中的符号引用替换为直接引用
-
初始化
- 调用构造函数进行初始化
- 类初始化时机创建某个类
1. 创建类的实例
2. 类的静态变量,或者为静态变量赋值
3. 类的静态方法
4. 使用类的反射机制强制创建某个类或接口对应的java.lang.Class对象
5. 初始化某个类的子类
6. 直接使用java.exe来运行某个主类
-
类加载器
- 负责将.class文件加载到内存中,并为之生成class对象
-
类加载器的组成加载器
-
Bootstrap ClassLoader根类加载器
- 引导类加载器,负责java核心类的加载
- System,String等,在jdk的lib目录下的rt.jar文件中
-
Extension ClassLoader扩展类加载器
- JRE扩展目录中的jar目录
- jdk 下的jre下的lib下的ext目录
-
System ClassLoader系统加载器
- jvm启动时加载java命令的class文件。以及classpath环境变量所指定的jar
-
Bootstrap ClassLoader根类加载器
反射
- 定义: java反射机制是在运行状态,对应任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用任意一个方法和属性,这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。
-
Class类
- Class没有公共构造方法。
- Class对象是在加载类时由java虚拟机以及通过调用类加载器中的defineClass方法自动构造的
-
获取Class对象的三种方式(获取字节码对象--Class对象为 包名.类名)
- 通过Object对象的getClass()方法
- 通过类名.class直接获取
- 通过Class的方法forName(“类名”)
- 通过反射获取
-
通过反射获取构造方法并使用
-
返回单个
- public的:getConstructor(Class<?>...parameterTypes)
- 包含private的:getDeclaredConstructor(Class<?>...parameterTypes)
-
返回多个
- public的:getConstructors()
- 包含private的:getDeclaredConstructors()
-
返回单个
-
通过反射方式,获取构造方法,创建对象
- 获取到Class对象
- 获取指定的构造方法
- 通过构造Constructor中的方法,创建对象
Class c=Class.forName("com.peng.Person"): Constructor con=c.getConstructor(String.class,int.class); Object obj=con.newInstance("张三",12);
-
通过反射方式,获取私有构造方法,创建对象
-
AccessibleObject类是Filed,Method和Constructor的父类
- 它提供了将反射的对象标记在使用时取消默认java访问控制检查的能力
- setAccessible(true/false)true为取消权限,false不取消权限检查
-
步骤
1. 获取Class 2. 获取指定的构造方法 3. 暴力访问,通过setAccessible(true)方法 4. 通过构造方法类Constructor来创建对象
Class c=Class.forName("com.peng.Person"); Constructor con=c.getDeclaredConstructor(String.class,int.class); con.setAccessible(true); Object obj=con.newInstance("小明",12);
-
AccessibleObject类是Filed,Method和Constructor的父类
-
通过反射获取成员变量并使用
- 成员变量使用Filed表示
-
方法
- getFiled(String name)--public修饰的变量
- getDeclaredFiled(String name)--任意权限
- getFileds()--获取所有public的变量
- getDeclaredFileds()--获取所有变量
-
实例
Class c=Class.forName("com.peng.Person"); Fileds[] fileds=c.getDeclaredFileds(); for(Filed filed:fileds){ System.out.println(filed); }
Filed temp=Class.forName("com.peng.Person").getFiled("age");
-
Filed方法
- set(obj,值)
- get(obj)
Class c=Class.forName("com.peng.Person"); Constructor con=c.getConstructor(String.class); Object obj=con.newInstance("李四"); Filed filed_name=c.getFiled("name"); filed_name.set(obj,"王五"); filed_name.get(obj);
-
通过反射获取成员方法并使用
-
Method类
-
方法(Class类方法)
- getMethod(String name,Class<?>... paraterTypes)--public的有参数的方法
- getDeclaredMethod(String name,Class<?>... paraterTypes)--任意的的代参数的方法
- getMethods()--获取全部publi的方法
- getDeclaredMethods()--获取所有方法
-
方法(Class类方法)
-
使用
Class c=Class.forName("com.peng.Person"); Constructor con=c.getDeclaredConstructor(String.class); Object obj=con.newInstance("赵六"); Method method1 = c.getDeclaredMethod("addPerson",int.class); method1.setAccessible(true);//暴力访问--private Object reslute=cmethod1.invoke(obj,12);1. 获取Class对象 2. 获取构造方法 3. 通过构造方法,创建对象 4. 获取指定的方法 5. 执行找到的方法
-
Method类
练习
-
将已存在的ArrayList中添加String数据(泛型的擦除)
1. 创建ArrayList<Integer>对象 list 2. 往list对象中添加Integer对象 3. 获取ArrayList的Class 4. 获取add方法 5. 添加String数据
ArrayList<Integer> list = new ArrayList<Integer>(); list.add(1); Class c=Class.forName("java.util.ArrayList"); //这里的Object.class保证可以添加任何对象 Method addMethod=c.getMthod("add",Object.class); addMethod.invoke(list,"哈哈");
package com.peng.test; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.Scanner; /** * @author kungfu~peng * @data 2017年10月9日 * @description */ public class Demo1 { public static void main(String[] args) throws Exception { ArrayList<Integer> list=new ArrayList<Integer>(); list.add(1); Class c=Class.forName("java.util.ArrayList"); Method me=c.getMethod("add", Object.class); me.invoke(list, "hehge"); System.out.println(list); } }
-
反射配置文件创建对象
-
读取配置文件的过程
1. Properties prop=new Properties(); 2. prop.load(new FileInputStream("person.properties")); 3. String name=prop.getProperty("name");
-
创建对象
1. Class对象 2. 构造函数创建对象
-
读取配置文件的过程
总结
-
如何获取Class对象
- 通过Object的getClass()方法
- 类名.class
- 反射Class.forName(String className)
-
通过反射,获取类中的构造函数
- Class的对象.getConstructor(参数)
-
通过反射,new对象
- 构造对象.newInstance(实参);
-
通过反射,找到方法并执行
- Class对象.getMethod(方法名,方法参数);
- Method对象.invoke(obj,方法实参);
-
通过反射,获取成员变量
- Class对象.Filed(变量名称);
-
Filed对象的方法
- set(obj,实参)
- get(obj)
练习
package testt;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
/**
* @author kungfu~peng
* @data 2017年10月10日
* @description
*/
public class Person {
private int age;
protected String name;
String sex;
public int id;
public Person() {
super();
// TODO Auto-generated constructor stub
}
public Person(int age, String name, String sex, int id) {
super();
this.age = age;
this.name = name;
this.sex = sex;
this.id = id;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
// 方法
/**
* 功能: 返回值类型: 参数列表:
*/
private void add() {
System.out.println("add");
}
/**
* 功能: 返回值类型: 参数列表:
*/
protected void delete() {
System.out.println("delete");
}
/**
* 功能: 返回值类型: 参数列表:
*/
void update() {
System.out.println("update");
}
/**
* 功能: 返回值类型: 参数列表:
*/
public void query(int id) {
System.out.println("query");
}
// 测试
public static void main(String[] args) throws Exception {
// 获取Class对象
Class c = Class.forName("testt.Person");
// 获取构造方法
Constructor con = c.getConstructor(int.class, String.class,
String.class, int.class);
// 通过构造方法来创建对象
Object obj = con.newInstance(12, "aa", "bb", 23);
// 获取属性
Field id_Filed = c.getDeclaredField("id");
Field name_Filed = c.getDeclaredField("name");
Field sex_Filed = c.getDeclaredField("sex");
Field age_Filed = c.getDeclaredField("age");
name_Filed.setAccessible(true);
id_Filed.setAccessible(true);
sex_Filed.setAccessible(true);
age_Filed.setAccessible(true);
// 设置属性
id_Filed.set(obj, 100);
name_Filed.set(obj, "100");
sex_Filed.set(obj, "100");
age_Filed.set(obj, 100);
// 获取属性值
System.out.println(id_Filed.get(obj));
// 获取方法
Method me = c.getDeclaredMethod("add", null);
me.setAccessible(true);
me.invoke(obj, null);
}
}
动态代理
-
代理是什么
-
我们大家都知道微商代理,简单地说就是代替厂家卖商品,厂家“委托”代理为其销售商品。关于微商代理,首先我们从他们那里买东西时通常不知道背后的厂家究竟是谁,也就是说,“委托者”对我们来说是不可见的;其次,微商代理主要以朋友圈的人为目标客户,这就相当于为厂家做了一次对客户群体的“过滤”。我们把微商代理和厂家进一步抽象,前者可抽象为代理类,后者可抽象为委托类(被代理类)。通过使用代理,通常有两个优点,并且能够分别与我们提到的微商代理的两个特点对应起来:
- 优点一:可以隐藏委托类的实现;
- 优点二:可以实现客户与委托类间的解耦,在不修改委托类代码的情况下能够做一些额外的处理。
-
我们大家都知道微商代理,简单地说就是代替厂家卖商品,厂家“委托”代理为其销售商品。关于微商代理,首先我们从他们那里买东西时通常不知道背后的厂家究竟是谁,也就是说,“委托者”对我们来说是不可见的;其次,微商代理主要以朋友圈的人为目标客户,这就相当于为厂家做了一次对客户群体的“过滤”。我们把微商代理和厂家进一步抽象,前者可抽象为代理类,后者可抽象为委托类(被代理类)。通过使用代理,通常有两个优点,并且能够分别与我们提到的微商代理的两个特点对应起来:
-
静态代理是什么
-
若代理类在程序运行前就已经存在,那么这种代理方式被成为 静态代理 ,这种情况下的代理类通常都是我们在Java代码中定义的。 通常情况下, 静态代理中的代理类和委托类会实现同一接口或是派生自相同的父类。
//接口 public interface Sell { void sell(); void ad(); } //生产厂家 public class Vendor implements Sell { public void sell() { System.out.println("In sell method"); } public void ad() { System,out.println("ad method") } } //微商代理 public class BusinessAgent implements Sell { private Vendor mVendor; public BusinessAgent(Vendor vendor) { this.mVendor = vendor; } public void sell() { mVendor.sell(); } public void ad() { mVendor.ad(); } public void sell() { if (isCollegeStudent()) { vendor.sell(); } } }
-
-
动态代理是什么
- 代理类在程序运行时创建的代理方式被成为动态代理。 也就是说,这种情况下,代理类并不是在Java代码中定义的,而是在运行时根据我们在Java代码中的“指示”动态生成的。相比于静态代理, 动态代理的优势在于可以很方便的对代理类的函数进行统一的处理,而不用修改每个代理类的函数。
-
例子
//静态代理--冗余代码 public class BusinessAgent implements Sell { private Vendor mVendor; public BusinessAgent(Vendor vendor) { this.mVendor = vendor; } public void sell() { System.out.println("before"); //冗余处 mVendor.sell(); System.out.println("after"); //冗余处 } public void ad() { System.out.println("before"); //冗余处 mVendor.ad(); System.out.println("after"); //冗余处 } } 从以上代码中我们可以了解到,通过静态代理实现我们的需求需要我们在每个方法中都添加相应的逻辑, 这里只存在两个方法所以工作量还不算大,假如Sell接口中包含上百个方法呢?这时候使用静态代理就会编写许多冗余代码。 通过使用动态代理,我们可以做一个“统一指示”,从而对所有代理类的方法进行统一处理,而不用逐一修改每个方法。
-
例子
-
InvocationHandler接口
-
在使用动态代理时,我们需要定义一个位于代理类与委托类之间的中介类,这个中介类被要求实现InvocationHandler接口
public interface InvocationHandler { Object invoke(Object proxy, Method method, Object[] args); }
- 从InvocationHandler这个名称我们就可以知道,实现了这个接口的中介类用做“调用处理器”。当我们调用代理类对象的方法时,这个“调用”会转送到invoke方法中,代理类对象作为proxy参数传入,参数method标识了我们具体调用的是代理类的哪个方法,args为这个方法的参数。这样一来,我们对代理类中的所有方法的调用都会变为对invoke的调用,这样我们可以在invoke方法中添加统一的处理逻辑(也可以根据method参数对不同的代理类方法做不同的处理)。因此我们只需在中介类的invoke方法实现中输出“before”,然后调用委托类的invoke方法,再输出“after”。下面我们来一步一步具体实现它。
-
动态代理中的委托类:动态代理方式下,要求委托类必须实现某个接口,这里我们实现的是Sell接口。委托类Vendor类的定义如下:
public class Vendor implements Sell { public void sell() { System.out.println("In sell method"); } public void ad() { System,out.println("ad method") } }
-
中介类:上面我们提到过,中介类必须实现InvocationHandler接口,作为调用处理器”拦截“对代理类方法的调用。中介类的定义如下:
public class DynamicProxy implements InvocationHandler { private Object obj; //obj为委托类对象; public DynamicProxy(Object obj) { this.obj = obj; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("before"); Object result = method.invoke(obj, args); System.out.println("after"); return result; } }
- 从以上代码中我们可以看到,中介类持有一个委托类对象引用,在invoke方法中调用了委托类对象的相应方法,看到这里是不是觉得似曾相识?通过聚合方式持有委托类对象引用,把外部对invoke的调用最终都转为对委托类对象的调用。这不就是我们上面介绍的静态代理的一种实现方式吗?实际上,中介类与委托类构成了静态代理关系,在这个关系中,中介类是代理类,委托类就是委托类; 代理类与中介类也构成一个静态代理关系,在这个关系中,中介类是委托类,代理类是代理类。也就是说,动态代理关系由两组静态代理关系组成,这就是动态代理的原理。下面我们来介绍一下如何”指示“以动态生成代理类。
-
动态生成代理类:
public class Main { public static void main(String[] args) { //创建中介类实例 DynamicProxy inter = new DynamicProxy(new Vendor()); //加上这句将会产生一个$Proxy0.class文件,这个文件即为动态生成的代理类文件 System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles","true"); Sell sell = (Sell)(Proxy.newProxyInstance(Sell.class.getClassLoader(), new Class[] {Sell.class}, inter)); //通过代理类对象调用代理类方法,实际上会转到invoke方法调用 sell.sell(); sell.ad(); } }
-
- 总结:首先通过newProxyInstance方法获取代理类实例,而后我们便可以通过这个代理类实例调用代理类的方法,对代理类的方法的调用实际上都会调用中介类(调用处理器)的invoke方法,在invoke方法中我们调用委托类的相应方法,并且可以添加自己的处理逻辑。
-
内省
- 内省:内省基于反射实现,主要用于操作JavaBean,通过内省 可以获取bean的getter/setter
-
内省与反射:
- 反射就是运行时获取一个类的所有信息,可以获取到.class的任何定义的信息(包括成员 变量,成员方法,构造器等) 可以操纵类的字段、方法、构造器等部分。
- 生活中 反射就像我们照镜子,照镜子时候 你的所有信息会毫无出错毫无保留的反射到镜子中,而java中反射就像是运行时用一把镜子去照.class字节码 将这个类的所有信息照出来,‘照’出的结果是客观的,是正确的;
- 内省就像我们反省自己,通常我们是针对犯错而进行反省,根据所犯错误反省总结出结论,这个结论是主观的,不一定正确的,有时候你觉得你自己做错了,但可能事实上自己无可厚非。java中内省,是针对javaBean进行的,目的是为了找出bean的getter和setter以便操作这个bean。只要看到有getter或者setter 就认为这个类有那么一个字段,比如看到getName() 内省就会认为这个类中有name字段,但事实上并不一定会有name;
-
直接通过属性的描述器java.beans.PropertyDescriptor类,来访问属性的getter/setter 方法;
public class Point { private Integer x; private Integer y; public Point(Integer x, Integer y) { super(); this.x = x; this.y = y; } public Integer getX() { return x; } public void setX(Integer x) { this.x = x; } public Integer getY() { return y; } public void setY(Integer y) { this.y = y; } } import java.beans.PropertyDescriptor; import java.lang.reflect.Method; public class Reflect { public static void main(String[] args) throws Exception { Point point = new Point(2, 5); String proName = "x"; getProperty(point, proName); setProperty(point, proName); } private static void setProperty(Point point, String proName) throws Exception { PropertyDescriptor proDescriptor = new PropertyDescriptor(proName, Point.class); Method methodSetX = proDescriptor.getWriteMethod(); methodSetX.invoke(point, 8); System.out.println(point.getX());// 8 } private static void getProperty(Point point, String proName) throws Exception { PropertyDescriptor proDescriptor = new PropertyDescriptor(proName, Point.class); Method methodGetX = proDescriptor.getReadMethod(); Object objx = methodGetX.invoke(point); System.out.println(objx);// 2 } }
-
通过类 Introspector 来获取某个对象的 BeanInfo 信息,然后通过 BeanInfo 来获取属性的描述器( PropertyDescriptor ),通过这个属性描述器就可以获取某个属性对应的 getter/setter 方法,然后我们就可以通过反射机制来调用这些方法。
private static void getProperty(Point point, String proName) throws Exception { BeanInfo beanInfo = Introspector.getBeanInfo(point.getClass()); PropertyDescriptor[] proDescriptors = beanInfo.getPropertyDescriptors(); for(PropertyDescriptor prop: proDescriptors){ if(prop.getName().equals(proName)){ Method methodGetx = prop.getReadMethod(); System.out.println(methodGetx.invoke(point));//8 break; } } }
下一篇: 玉米煮多长时间会熟?玉米可以怎么吃?