概述
------
android培训
、
java培训
,期待与您交流 ------
黑马程序员 java之可变参数枚举反射内省动态代理
一:可变参数
可变参数什么情况下使用
我们有时在调用方法时并不知道具体的参数个数,要动态的传入。有一种方法,就是写多个重载方法。
public void add(int a){}
public void add(int a,int b){}
public void add(int a,int b,int c){}
这么做只是解决部分问题,并没有找到解决问题的通用方法,不是一种好方法,灵活性扩展性一点也不好
可变参数就很好的解决了这一问题
下面是一个求和的方法,参数个数任意,返回所有传入参数的和
完整代码如下
package cn.itcast;
public class VariableParameter {
public static void main(String[] args) {
System.out.println(add(1,5,8));
}
public static int add(int a,int... args){
int sum =a;
for (int i = 0; i < args.length; i++) {
sum=sum+args[i];
}
return sum;
}
}
可变参数的简单语法格式为:
methodName(ordinaryArgument, dataType... argumentName);
其中:
ordinaryArgument——非可变参数,就是能确定的参数,这个可有可无。
dataType——要传入的可变参数的数据类型
需要的操作符。表示0到多个,用3个点表示。
argumentName——可变参数的参数名。
要注意的是可变参数必须在最后,因为其后若有非可变参数,也会被当成可变参数
二:枚举
在程序设计中,有时需要用到的元素是有限的且需作出限制,调用者不能随便创建其他元素,
如一周内的星期一到星期日七个数据元取值仅限于{Sun,Mon,Tue,Wed,Thu,Fri,Sat}集合中的元素。
此时,可将这些数据集合定义为枚举类型。因此,枚举类型是某类数据可能取值的集合
枚举的基本格式:
enum <枚举类型名>
{ <枚举元素表> };
如 enum weekdays
{ Sun,Mon,Tue,Wed,Thu,Fri,Sat };
枚举的特性:
是一个特殊形式的类,但跟普通类一样,也可以声明属性,方法,构造函数
枚举中声明的每一个变量代表一个实例对象
枚举因为不能让使用者再创建对象,因此构造需私有。
枚举中常用方法:
String name() 返回此枚举常量的名称,在其枚举声明中对其进行声明。
int ordinal() 返回枚举常量的序数(它在枚举声明中的位置,其中初始常量序数为零)。
Class<E> getDeclaringClass() 返回与此枚举常量的枚举类型相对应的 Class 对象。
static valueOf( enumType, String name) 返回带指定名称的指定枚举类型的枚举常量
object[] values() 返回所有枚举常量
枚举中如何封装信息,如何获取信息?通过构造函数可封装和对外提供公有方法获取。
如学生成绩分为 A B C D E 五个等级,在获取等级的时候也获取对应的分数段
package cn.itcast;
public class EnumDemo {
public static void main(String[] args) {
System.out.println(Grade.A.getValue());
}
}
enum Grade{
A("90-99"),B("80-89"),C("70-79"),D("60-69"),E("0-59");
private String value;//封装对象对应的值
private Grade(String value){
this.value=value;
}
public String getValue(){//对外提供一个方法获取值
return this.value;
}
}
带有抽象方法的枚举:
如对于交通灯,绿灯的下一个灯返回黄灯,黄灯下一个返回灯红灯,红灯下一个返回绿灯
每一个对象的具体操作不尽相同,这是我们可以将该方法声明为抽象的,并在每个单独对象中予以实现.
package cn.itcast;
enum Lamp {
red(){
//对象自己实现抽象方法:
@Override
public Lamp getNextLamp() {
return green;
}},
green(){
@Override
public Lamp getNextLamp() {
return yellow;
}},
yellow(){
@Override
public Lamp getNextLamp() {
return red;
}};
public abstract Lamp getNextLamp();
}
三:反射
反射就是将类加载进内存并解剖该类,通过反射能获取该类中的构造函数,属性,方法。
AIP中的Class类就代表某个类的字节码,获取这个Class对象,有三种方式
1:通过每个对象都具备的方法getClass来获取。弊端:必须要先有该类对象,才可以调用getClass方法。
2:每一个数据类型(基本数据类型和引用数据类型)都有一个静态的属性class。弊端:必须要先明确该类。
3:使用的Class类中的静态的forName方法,这种方式的扩展性最强,只要将类名的字符串传入即可。
static Class<?> forName(String className) 返回与带有给定字符串名的类或接口相关联的 Class 对象。
对于解剖加载进来的类,Class提供了一系列方法用于获取构造函数,属性,方法:
获取构造函数: Constructor<T> getConstructor(Class<?>... parameterTypes)
返回一个 Constructor 对象,它反映此 Class 对象所表示的类的指定公共构造方法。
Constructor<?>[] getConstructors()
返回一个包含某些 Constructor 对象的数组,这些对象反映此 Class 对象所表示的类的所有公共构造方法。
Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes)
返回一个 Constructor 对象,该对象反映此 Class 对象所表示的类或接口的指定构造方法,包括私有。
获取属性
Field getField(String name)
返回一个Field对象,它反映此Class对象所表示的类或接口的指定公共成员字段。
Field[] getFields()
返回一个包含某些Field对象的数组,这些对象反映此 Class 对象所表示的类或接口的所有可访问公共字段。
Field[] getDeclaredFields()
返回 Field 对象的一个数组,这些对象反映此 Class 对象所表示的类或接口所声明的所有字段,包括私有。
方法获取
Method getMethod(String name, Class<?>... parameterTypes)
返回一个Method对象,它反映此Class对象所表示的类或接口的指定公共成员方法。
Method[] getMethods()
返回一个包含某些 Method 对象的数组,这些对象反映此 Class 对象所表示的类或接口的公共 member 方法。
Method getDeclaredMethod(String name, Class<?>... parameterTypes)
返回一个 Method 对象,该对象反映此 Class 对象所表示的类或接口的指定已声明方法,包括私有。
反射的具体运用
package cn.itcast;
public class Person {
private String name ="zhangwuji";
private int age=19;
private char sex;
public Person(){}
private Person(String name, int age, char sex) {
this.name = name;
this.age = age;
this.sex = sex;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public char getSex() {
return sex;
}
public void setSex(char sex) {
this.sex = sex;
}
public int aa(int num){
return num;
}
}
package cn.itcast;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import org.junit.Test;
public class ReflectDemo {
@Test
public void testReflect() throws Exception {
Class clazz = Class.forName("cn.itcast.Person");
// 解剖构造函数
Constructor c = clazz.getConstructor(String.class, int.class,char.class);
//获取构造函数后,根据参数创建相应对象
Person p = (Person) c.newInstance("赵敏", 28, '女');
System.out.println(p.getName() + "," + p.getAge() + "," + p.getSex());
// 字段的反射,参数是指定的字段的名称
Field f = clazz.getField("age");
System.out.println(f.get(p));
// 解剖方法,参数分别是指定方法名,方法参数类型
Method m = clazz.getMethod("aa", int.class);
// 方法运行,参数分别是指定的对象,参数值
System.out.println(m.invoke(p, 6));
}
}
对于私有的构造函数,字段和方法,需要使用带有Declared的方法进行反射,并设置 setAccessible(true),是暴力反射。
四:内省
内省主要用来操作javaBean的属性封装数据。
Introspector 类为通过工具学习有关受目标Java Bean支持的属性、事件和方法的知识提供了一个标准方法。
API提供的相关方法摘要:
static BeanInfo getBeanInfo(Class<?> beanClass) 在 Java Bean 上进行内省,了解其所有属性、公开的方法和事件。
PropertyDescriptor[] getPropertyDescriptors() 获得 beans PropertyDescriptor,属性描述器。
Method getReadMethod() 获得应该用于读取属性值的方法。
Method getWriteMethod() 获得应该用于写入属性值的方法。
PropertyDescriptor(String propertyName, Class<?> beanClass) ,一构造就指定操作指定Bean的指定属性
操作示例:
package cn.itcast;
import java.beans.BeanInfo;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Method;
import org.junit.Test;
public class IntrospectorDemo {
@Test
public void testInTrospector() throws Exception{
//获取该bean自己的属性
BeanInfo bf =Introspector.getBeanInfo(Person.class,Object.class);
//得到所有的属性描述器
PropertyDescriptor[] pds=bf.getPropertyDescriptors();
for(PropertyDescriptor pd: pds){
System.out.println(pd.getName());
}
//操作bean指定的属性 name
PropertyDescriptor pd=new PropertyDescriptor("name", Person.class);
//得到属性的写方法,为属性赋值
Method method_w=pd.getWriteMethod();//相当于获取了 name的 setName()方法
Person p =(Person) Class.forName("cn.itcast.Person").newInstance();
method_w.invoke(p, "赵敏");
//得到属性的读方法,获取属性的值
Method method_r=pd.getReadMethod();// 相当于得到了naem的getName()方法
System.out.println(method_r.invoke(p, null));
}
}
操作bean的属性在开发中经常用到,因此apache组织提供了一个工具类BeanUtils来操作bean。
package cn.itcast;
import org.apache.commons.beanutils.BeanUtils;
import org.junit.Test;
public class BeanUtilsDemo {
@Test
public void testBeanUtils1() throws Exception{
Person p =new Person();
//给指定的对象的指定的属性赋值
BeanUtils.setProperty(p, "name", "赵敏");
System.out.println(p.getName());
}
@Test
public void testBeanUtils2() throws Exception{
//能用于接收客户端数据,内部自动完成类型转换
//模拟客户端数据
String name="张无忌";
int age =25;
char sex='男';
Person p =new Person();
//将数据封装进bean实体
BeanUtils.setProperty(p, "name", name);
BeanUtils.setProperty(p, "age", age);
BeanUtils.setProperty(p, "sex", sex);
System.out.println(p.getName()+":"+p.getAge()+","+p.getSex());
}
}
BeanUtils只支持基本数据类型的转换,因此对于某些特定类型如日期类型的转换,需要我们自己给BeanUtils注册转换器
package cn.itcast;
import java.sql.Date;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import org.apache.commons.beanutils.BeanUtils;
import org.apache.commons.beanutils.ConvertUtils;
import org.apache.commons.beanutils.Converter;
import org.junit.Test;
public class BeanUtilsDemo {
@Test
public void testBeanUtils1() throws Exception{
Person p =new Person();
//给指定的对象的指定的属性赋值
BeanUtils.setProperty(p, "name", "赵敏");
System.out.println(p.getName());
}
@Test
public void testBeanUtils2() throws Exception{
//能用于接收客户端数据,内部自动完成类型转换
//模拟客户端数据
String name="张无忌";
int age =25;
char sex='男';
Person p =new Person();
//将数据封装进bean实体
BeanUtils.setProperty(p, "name", name);
BeanUtils.setProperty(p, "age", age);
BeanUtils.setProperty(p, "sex", sex);
System.out.println(p.getName()+":"+p.getAge()+","+p.getSex());
}
@Test
public void testBeanUtils3() throws Exception{
//能用于接收客户端数据,内部自动完成类型转换
//模拟客户端数据
String name="张无忌";
String birthday="2000-01-01";
//日期类型不支持转换,需要注册转换器
ConvertUtils.register(new Converter() {
@Override
public Object convert(Class type, Object value) {
//BeanUtils转换时会调用该方法
//将客户端String类型的value转为日期
String str=(String) value;
SimpleDateFormat sdf=new SimpleDateFormat("yyyy-MM-dd");
try {
return sdf.parse(str);
} catch (ParseException e) {
throw new RuntimeException(e);
}
}
},Date.class);
Person p =new Person();
//将数据封装进bean实体
BeanUtils.setProperty(p, "name", name);
BeanUtils.setProperty(p, "birthday",birthday );
System.out.println(p.getName()+":"+p.getBirthday());
}
}
五:动态代理
动态代理的理解:
一个类在实现目标方法(业务逻辑)的时候,还需要添加一些其他的功能,如安全检查,权限验证,日志记录灯,
如调用每一个方法时都需要做这些额外的事,代码重复性高,这些额外功能就是切面代码。这时可将这些额外的
事情交给一个与目标实现类相同接口的代理类管理,当代理类对象调用方法时,它不仅实现目标方法,还
会自动完成这些额外的功能。
JVM可以在运行期动态生成出类的字节码,这种动态生成的类往往被用作代理类,即动态代理类。
后面可以看到,为避免硬编码,将额外功能封装成独立的功能模块作为参数和目标类对象一同传递给代理类。
proxy类提供的代理对象的获取:
static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
返回一个指定接口的代理类实例,该接口可以将方法调用指派到指定的调用处理程序。
代理实现原理:
该代理类的构造方法接收一个类型为InvocationHandler的子类对象,这个类其实就是目标类,将这个子类对象通过参数
传给代理类,返回一个代理类对象,代理对象调用和子类中相同的某一方法时,会自动执行invocationHandler接口实现类
中的invoke方法,在这个方法中就会进行额外事物的处理,而目标方法的实际调用还是由子类对象完成的,并把结果返回给
代理对象,也就是说,子类对象只需要关注业务逻辑,其他事由代理对象完成。
下面是一个代理模式的实现:
package cn.itcast;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.ArrayList;
import java.util.Collection;
import org.junit.Test;
public class ProxyDemo {
@Test
public void testProxy() {
ArrayList target = new ArrayList();
Collection proxy =(Collection) getProxy(target,new AdviceImpl());
proxy.add("zxx");
proxy.add("bxd");
}
/*
* 为避免硬编码,将目标对象和其他功能(封装成对象)
* 作为参数传递*/
private static Object getProxy(final Object target,final Advice advice) {
Object proxy = Proxy.newProxyInstance(//
target.getClass().getClassLoader(),//目标类类加载器
target.getClass().getInterfaces(),//与target类实现相同接口
//匿名内部类实现接口
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method,
Object[] args) throws Throwable {
//额外的事情作为参数传进来可在这里处理
advice.beforeMethod();
//这里进行业务逻辑的处理
Object value = method.invoke(target, args);
return value;
}
});
return proxy;
}
}
------
android培训
、
java培训
,期待与您交流 ------
最后
以上就是害羞长颈鹿为你收集整理的JDK1.5之可变参数枚举反射内省动态代理的全部内容,希望文章能够帮你解决JDK1.5之可变参数枚举反射内省动态代理所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复