------- android培训、java培训、期待与您交流! ----------
MyEclipse相关知识
Workspace与project
切换工作空间:File---Switch Workspace---Other
一个工作间包含多个工程,切换工作间之后,会影响里面的工程,例如快捷键什么的都要重新配置如果重新配了之后快捷键还没有用就要考虑下那个快捷键是不是冲突了
视图管理与程序调试
可以通过Window---Show View来添加各种透视图。
调试程序的时候可以在代码右边双击,然后选择Debugs As,就会弹出Debug的透视图,然后选择要查看的变量,右键选择watch即可。
设置单个工程的javac与java。
高版本的java能否运行低版本的javac编译的程序?能
低版本的java能否运行高版本的javac编译的程序?不能
运行版本(java)比编译版本低(javac)的话不能运行会出现"Bad version number in .class file"问题
总之能设置整个workspace的javac与java,也能设置单个工程的javac与java。
快捷键的绑定与代码模板
新建模板步骤:选择代码---右键Surround With---Configure Template--- new...
利用快捷键提高工作效率;
导别人的包的时候,别人编写代码的时候的jre可能和你机器上的不一样所以要把原来的给删掉,换成自己的JDK版本。
步骤:选中工程---右键选择Build Path---Configure Build Path---Libraries---remove掉
然后再按这个步骤选 Add library---JRE System library。
-------------------------------------------------------------
java5的静态导入
import语句可以导入一个类或某个包中的所有类
import static是JDK1.5的新特性,它的意思是导入一个类中的某个静态方法或所有静态方法。
package cn.itcast.day1; //import static java.lang.Math.max; import static java.lang.Math.*; public class StaticImport { public static void main(String[] args) { // TODO Auto-generated method stub int x =1; x++; System.out.println(x); System.out.println(max(3, 6)); System.out.println(abs(3 - 6)); } }
--------------------------------------------------------------------
可变参数与OverLoad
1、可变参数
(1)、只能出现在参数列表的最后;
(2)、...(三个点)位于变量类型和变量名之间,前后有无空格都可以
(3)、调用可变参数的方法时,编译器为该可变参数隐含创建的一个数组,在方法中一数组的形式访问可变参数,即JKD5.0以前都是用数组的方式进行传递参数。
package cn.itcast.day1; public class VariableParameter { public static void main(String[] args) { // TODO Auto-generated method stub System.out.println(add(2,4)); System.out.println(add(2,5,8)); } public static int add(int x,int... args){ int sum = x; /* for(int i=0;i<args.length;i++){ sum += args[i]; }*/ for(int arg:args){ sum += arg; } return sum; } }
2、overload和override
OverLoad 重载 只看参数类型或个数。
Override 重写 父类方法私有的话,子类再写一个同样的函数不叫重写,那是一个全新的方法。
-----------------------------------------------------------------------------
增加for循环
1、语法:
for(type 变量名 : 集合变量名){}
2、注意事项
迭代变量必须在()中定义
集合变量可以使数组或实现了Iterable接口的集合类。
代码示例参考可变参数示例。
--------------------------------------------------------------------------------------------
基本数据的自动拆装箱及享元设计模式
package cn.itcast.day1; public class AutoBox { public static void main(String[] args){ Integer iObj = 3; System.out.println(iObj+12); Integer i1 = 13; Integer i2 = 13; Integer i3 = 137; Integer i4 = 137; //在-128~127区间之间,同意个对象。 //设计模式:享元模式。flyweight System.out.println(i1==i2); System.out.println(i3==i4); } }
享元设计模式(自我理解):就是将一些“零碎”,但使用频率较高的东西进行共享。比如一个办公室有多台电脑需要连接打印机,但是打印机的数据接口只有一个,所以只要把连接打印机的电脑上的打印机进行共享,全办公室都可以使用了。
----------------------------------------------------------------------
枚举
1、枚举就是让某个类型的变量的取值只能为若干个固定值中的一个,否则,编译器报错。
枚举可以让编译器在编译时就可以控制源程序中填写的非法值,普通变量的方式在开发阶段无法实现这一目标。
2、枚举就是一种特殊的类,其中的每个元素都是该类中的一个实例对象
用普通类模拟枚举
package cn.itcast.day1; public abstract class WeekDay1 { private WeekDay1(){} public final static WeekDay1 SUN = new WeekDay1(){ @Override public WeekDay1 nextDay() { // TODO Auto-generated method stub return MON; } }; public final static WeekDay1 MON = new WeekDay1(){ @Override public WeekDay1 nextDay() { // TODO Auto-generated method stub return SUN; } }; public abstract WeekDay1 nextDay(); //将类定义成抽象类,能将if else语句转换成内部类。 /* public WeekDay nextDay(){ if(this == SUN) return MON; else return SUN; }*/ public String toString(){ return this==SUN?"SUN":"MON"; } }
枚举的基本应用
package cn.itcast.day1; import java.util.Date; public class EnumTest { public static void main(String[] args) { // TODO Auto-generated method stub WeekDay1 weekDay = WeekDay1.MON; System.out.println(weekDay.nextDay()); WeekDay weekDay2 = WeekDay.FRI; System.out.println(weekDay2); System.out.println(weekDay2.name()); System.out.println(weekDay2.ordinal());//返回枚举常量的序数(它在枚举声明中的位置,其中初始常量序数为零)。 System.out.println(WeekDay.valueOf("SUN").toString()); System.out.println(WeekDay.values().length); // 建立Date子类。调用父类的有参构造方法。 new Date(300){}; } //枚举只有一个成员时,就可以作为一种单例的实现方式。 //如果写单例,可以用枚举。枚举是一个特殊的类,默认私有构造函数,自动创建对象。 public enum WeekDay{ SUN(1),MON(),TUE,WED,THU,FRI,SAT;//当元素类别之后又内容时,要加“;” //必须要定义在元素列表之后。 private WeekDay() {System.out.println("first");} private WeekDay(int day){System.out.println("second"); } } public enum TrafficLamp{ //enum的子类。复写nextLamp方法。 RED(45) { @Override public TrafficLamp nextLamp() { // TODO Auto-generated method stub return GREEN; } }, GREEN (30){ @Override public TrafficLamp nextLamp() { // TODO Auto-generated method stub return YELLOW; } }, YELLOW (3){ @Override public TrafficLamp nextLamp() { // TODO Auto-generated method stub return RED; } }; public abstract TrafficLamp nextLamp(); private int time; private TrafficLamp(int time){this.time = time;} } }
--------------------------------------------------------------------
反射
反射就是把java类中的各种成分映射成相应的java类。
1、java程序中的各个java类属于同一类事物,描述这类事物的java类名就是Class
(1)、java类用于描述一类事物的共性,该类事物有什么属性,没有什么属性,至于这个属性的值施舍呢么,则是由这个类的实例对象来确定的,不同的实例对象有不同的属性值。Java程序中的各个java类,他们是否属于同一类事物,是不是可以用一个类来描述这类事物呢?这个类的名字就是Class,要注意与小写class关键字的区别。Class类描述哪些方面的信息呢?类的名字,类的访问属性,类所属的包名,字段名称的列表,方法名称的类表,等等,学习反射,首先就要明白Class这个类
2、对比提问:众多的人用一个什么类表示?众多的java类用一个什么类表示?
(1)、人->Person
(2)、Java类->Class
3、对比提问:Person类代表人,他的实例对象就是张三,李四这样一个个具体的人,Class类代表Java类,它的各个实例对象又分别对应什么呢?
(1)、对应各个类在内存中的字节码,例如Person类的字节码,ArrayList类的字节码,等等
(2)、一个类被类加载器加载到内存中,占用一片存储空间,这个空间里面的内容就是累的字节码,不同的类的字节码是不同的,所以它们在内存中是不同的,这一个个的空间可分别用一个个的对象来表示这些对象显然具有相同的类型,这个类型是什么呢?
4、如何得到各个字节码对应的实例对象(Class类型)
(1)、类名.class,例如,System.class
(2)、对象.getClass(),例如,new Date().getClass()
(3)、Class.forName("类名"),例如,Class.forName("java.util.Date")
5、九个预定义Class实例对象
(1)、参看Class.isPrimitive()方法的帮助,是否为基本类型
(2)、Int.class==Integer.TYPE//表示基本类型 int 的 Class 实例。
(3)、Class 类的实例表示正在运行的 Java 应用程序中的类和接口。枚举是一种类,注释是一种接口。
每个数组属于被映射为 Class 对象的一个类,所有具有相同元素类型和维数的数组都共享该 Class 对象。
基本的 Java 类型(boolean、byte、char、short、int、long、float 和 double)和关键字 void 也表示为 Class 对象。
(4)、int.class==Integer.TYPE , isPrimitive public boolean isPrimitive()判定指定的 Class 对象是否表示一个基本类型。
有九种预定义的 Class 对象,表示八个基本类型和 void。这些类对象由 Java 虚拟机创建,与其表示的基本类型同名,即 boolean、byte、char、short、int、long、float 和 double。
这些对象仅能通过下列声明为 public static final 的变量访问,也是使此方法返回 true 的仅有的几个 Class 对象。
6、数组类型的、class实例对像
Class.isArray()
总之,只要是在源程序中出现的类型,都有各自的Class实例对象,例如,int[] void
public class ReflectTest { public static void main(String[] args)throws Exception{ String str1 = "abc"; Class cls1 = str1.getClass(); Class cls2 = String.class; Class cls3 = Class.forName("java.lang.String"); System.out.println(cls1 ==cls2);//true System.out.println(cls1 ==cls3);//true System.out.println(cls1.isPrimitive());//false System.out.println(int.class.isPrimitive());//true System.out.println(int.class == Integer.class);//false System.out.println(int.class == Integer.TYPE);//true System.out.println(int[].class.isPrimitive());//false System.out.println(int[].class.isArray());//true } }
构造方法的反射
1、Constructor类代表某个类中的一个构造方法
T newInstance(Object... initargs) 使用此 Constructor 对象表示的构造方法来创建该构造方法的声明类的新实例,并用指定的初始化参数初始化该实例
2、得到某个类所有的构造方法:
例子:Constructor[] constructor = Class.forName("java.lang.String").getConstructors();
例子:Constructor[] constructor = Class.forName("java.lang.String").getConstructors(StringBuffer.class);
3、创建实例对象
通常方式:String str = new String(new StringBuffer("abc"));
反射方式:String str = (String)constructor.newInstance(new StringBuffer("abc"));
调用获得的方法时要用到上面相同类型的实例对象
4、Class.newInstance()方法
例子:String obj = (String)Class.forName("java.lang.String").newInstance();
该方法内部先得到默认的构造方法,然后用该构造方法创建实例对象
该方法内部具体代码是怎样写的呢?用到了缓存机制来保存默认构造方法的实例对象
//获取String中的某个构造方法。 //new String(new StringBuffer("abc")); 获得方法时用到的类型。 Constructor constructor = String.class.getConstructor(StringBuffer.class); //调用获得的方法时要用到上面相同类型的实例对象。 String str2 = (String)constructor.newInstance(/*"abc"*/new StringBuffer("abc")); System.out.println(str2.charAt(2));
成员变量的反射
1、Field类代表某个类中的一个成员变量
2、问题:得到的Field对象是对应到类上面的成员变量,还是对应发到对象上的成员变量?类只有一个,而该类的实例对象有多个,如果是与对象关联,那关联那个对象呢?所以字段field代表的是类的定义,而不是具体的x变量Field getDeclaredField(String name) 返回一个 Field 对象,该对象反映此 Class 对象所表示的类或接口的指定已声明字段。Field[] getDeclaredFields() 返回 Field 对象的一个数组,这些对象反映此 Class 对象所表示的类或接口所声明的所有字段。Field getField(String name) 返回一个 Field 对象,它反映此 Class 对象所表示的类或接口的指定公共成员字段。Field[] getFields() 返回一个包含某些 Field 对象的数组,这些对象反映此 Class 对象所表示的类或接口的所有可访问公共字段。
//反射获取Filed ReflectPoint pt1 = new ReflectPoint(2, 5); Field fieldY = pt1.getClass().getField("y"); //fieldY的值是多少?5,错。fieldY不是对象身上的变量,而是类上,要用他去获取摸个对象上对于的值。 System.out.println(fieldY.get(pt1)); Field fieldX = pt1.getClass().getDeclaredField("x"); fieldX.setAccessible(true);//暴力反射。将 System.out.println(fieldX.get(pt1)); changeStringValue(pt1); System.out.println(pt1); public class ReflectPoint { private int x; public int y; public String str1 ="ball"; public String str2 = "baskball"; public String str3 = "itcast"; public ReflectPoint(int x, int y) { super(); this.x = x; this.y = y; } }
例子
import java.lang.reflect.*; public class ReflectTest { public static void main(String[] args) throws Exception{ ReflectPoint pt1 = new ReflectPoint(3,5); changeStringValue(pt1); System.out.println(pt1); } private static void changeStringValue(Object obj) throws Exception { Field[] fields = obj.getClass().getFields(); for(Field field : fields){ if(field.getType()==String.class){ String oldValue = (String)field.get(obj); String newValue = oldValue.replace(‘b‘, ‘a‘); field.set(obj,newValue); } } } } class ReflectPoint { private int x; public int y; public String str1 = "ball"; public String str2 = "basketball"; public String str3 = "itcast"; public ReflectPoint(int x, int y) { super(); this.x = x; this.y = y; } public String toString() { return str1+":"+str2+":"+str3; } }
成员方法的反射
Method
1、Method类代表某个类中的一个成员方法
2、得到类中的某一个方法:
例子:Method charAt = Class.forName("java.lang.String").getMethod("charAt",int.class);
3、调用方法:
通常方式:System.out.println(str.charAt(1));
反射方式:System.out.println(charAt.intvoke(str,1))
如果传递给Method对象的invoke()方法的一个参数为null,这有着什么意义呢?说明该Method对象对应的是一个静态方法。静态方法法不需要对象。
import java.lang.reflect.*; public class ReflectTest { public static void main(String[] args) throws Exception{ Method methodCharAt = String.class.getMethod("charAt", int.class); System.out.println(methodCharAt.invoke("abc",1 )); //System.out.println(methodCharAt.invoke(null,1 ));如果为null则为静态方法 //System.out.println(methodCharAt.invoke("abc",new Object[]{2} ));1.5以前,没有可变参数,这么写 } }
对接收数组参数的成员方法进行反射
用反射方式执行某个类中的Main方法
1、目标:
写一个程序,这个程序能够根据用户提供的类名,去执行该类中的main方法。用普通方式调完后,大家要明白,为什么要用反射的方式去调呢?
因为不知道哪个主函数调用
2、问题
启动java程序的main方法的参数是一个字符串数组,即public static void main(String[] args),通过反射方式来调用这个main方法时,如何为invoke方法法传递参数呢?按jdk1.5的语法,整个数组是一个参数,而按jdk1.4的语法,数组中的每个元素对应一个参数,当把一个字符串数组作为参数传递给jdk1.4的语法时,javac会到底按照那种语法进行处理呢?jdk1.5肯定要兼容jdk1.4的语法,会按jdk1.4的语法进行处理,即把数组打散成若干个单独的参数,所以,在给main方法传递参数时,不能使用代码 mainMethod.invoke(null,new String[]{"xxx"}),javac只把它当做jdk1.4的语法进行理解,而不把它当做jdk1.5的语法解释,因此会出现参数类型不对的问题。
//不只是main方法,普通方法也一样
3、解决办法
mainMethod.invoke(null,new Object[]{new String[]{"sddsfs"}})
mainMethod.invoke(null,(Object)new String[]{"sddsfs"})编译器会作特殊处理,编译时不把参数当做数组看待,也就不会把数组打散成若个参数了
//调用main方法。 //TestArguments.main(new String[]{"111","222","333"}); //为什么要用反射的方式调用。 String startingClassName = args[0]; Method mainMethod = Class.forName(startingClassName).getMethod("main", String[].class); mainMethod.invoke(null,new Object[]{new String[]{"111","222","333"}}); //mainMethod.invoke(null,(Object)new String[]{"111","222","333"});效果同上。
反射的基本步骤:
1.通过那三种方式先获取字节码对象
2.实例化字节码对象,再获取字节码中的构造函数,方法以及变量
3.通过传参数等方法确定你要用的是哪个方法或者变量。
数组与Object的关系及其反射类型
Arrays.asList()方法处理int[]和String[]时的差异。
处理int[]时打印的是数组名和哈希值,处理S特ring[]时打印出来的是数组里面的元素。
就是集合里面装的是对象,那int[]数组转换为集合后不可能数组里面的元素就是集合里面的元素,
因为数组里面的是int型值不是对象,而String数组里面的字符串本来就是一个对象,所以转换为
集合后可以打印出来。
int[] a1 = new int[]{1,2,3}; int[] a2 = new int[4]; int[][] a3 = new int[2][3]; String[] a4 = new String[]{"a","b","c"}; System.out.println(a1.getClass() == a2.getClass());//true System.out.println(a1.getClass().equals(a4.getClass()));//false System.out.println(a1.getClass().equals(a3.getClass()));//false //为何会报错Incompatible operand types Class<capture#8-of ? extends int[]> and Class<capture#9-of ? extends String[]> //张老师视频中不会报错,而且能运行。 //System.out.println(a1.getClass() == a4.getClass()); //System.out.println(a1.getClass() == a3.getClass()); System.out.println(a1.getClass().getName());//[I int型一维数组 System.out.println(a1.getClass().getSuperclass());//class java.lang.Object System.out.println(a4.getClass().getSuperclass());//class java.lang.Object //多态 Object aObj1 = a1;//a1为数组实体。 Object aObj2 = a4; //Object[] aObj3 = a1;//a1为一维数组,内容为int型基本数据。 Object[] aObj4 = a3; Object[] aObj5 = a4; System.out.println(a1);//[I@773a1 System.out.println(a4);//[Ljava.lang.String;@1385846 System.out.println(Arrays.asList(a1));//[[I@773a1] System.out.println(Arrays.asList(a4));//[a, b, c]
private static void printObjtec(Object obj) { // TODO Auto-generated method stub Class clazz = obj.getClass();//获取字节码 if(clazz.isArray()){//判断是不是数组。 int len = Array.getLength(obj);//得到长度 for(int i=0;i<len;i++){ System.out.println(Array.get(obj, i)); } }else{ System.out.println(obj); } }
ArrayList_HashSet的比较及Hashcode分析
HashSet集合是不允许里面的元素有相同的。
哈希算法就是把集合分成了若干个区域,每一个要存进来的对象可以算出一个值,根据算出来的值,把它放到相应的区域里面去。当某个对象要存进来时,先算出它的HashCode值,根据它的哈希值判断,把对象放到它所对应的区域,这样对于查找某个对象直接算出他的HashCode值,这样不用挨个去排查,直接找出对象所在的区域即可,提高了效率。HashSet就是采用了哈希算法的集合,所以在HashSet中判断是否是同一个元素不仅要判断equals还要判断hashCode。
注意:当一个对象被存储进HashSet集合以后,就不要再改动这个对象中参与哈希值运算的变量了,否则会造成内存泄露。因为存进去之后,改变那个参与哈希值运算的变量的话,那这个对象的哈希值也就跟着变了,你再按这个哈希值删除对象的话会删不掉,因为哈希值一变你删除的就是另个区域的值了,而存进来的那个对象还依然在原位置存放着呢。
内存泄露:
所谓内存泄露就是有一个对象我不再用了,但它还一直占用着空间没有被释放。比如当把一个对象存进了
hashset集合以后,又改变了这个对象中参与计算哈希值的字段了
ReflectPoint pt1 = new ReflectPoint(3, 3); ReflectPoint pt2 = new ReflectPoint(5, 5); ReflectPoint pt3 = new ReflectPoint(3, 3); collections.add(pt1); collections.add(pt2); collections.add(pt3); collections.add(pt1); pt1.y = 7; collections.remove(pt1);//pt1不能移除,因为y值被改变,哈希值被改变。 //这种操作过多时,造成内存被占用,导致内存溢出。 System.out.println(collections.size()); } }
框架的概念及用反射技术开发框架的原理
由于框架不知道运行什么类和函数,所要使用反射
用类加载器的方式管理资源和配置文件例子:
获取配置的路径,要使用方法计算出来,getRealPath
//getRealPath();//金山词霸/内部 //一定要记住用完整的路径,但完整的路径不是硬编码,而是运算出来的。 //InputStream ips = new FileInputStream("config.properties"); //classpath路径。路径分隔可用\\,也可以用/ InputStream ips = ReflectTest2.class.getClassLoader().getResourceAsStream("cn/itcast/day1/config.properties"); //InputStream ips = ReflectTest2.class.getResourceAsStream("config.properties"); Properties props = new Properties(); props.load(ips); ips.close(); String className = props.getProperty("className"); Collection collections = (Collection) Class.forName(className).newInstance();
JavaBean内省
什么是 JavaBenn
JavaBean是一种特殊的Java类,主要用于传递数据信息,这种java类中的方法主要用于访问私有的字段,且方法名符合某种命名规则。JavaBenn中的方法要么是set开头要么是get开头,set或get之后的次就是JavaBenn的属性,如setAge(),那在JavaBenn中肯定有一个变量名是Age。
如果一个Java类中的一些方法符合某种命名规则,则可以把它当作JavaBean来使用。
总之,一个类被当作javaBean使用时,JavaBean的属性是根据方法名推断出来的,它根本看不到java类内部的成员变量。一个符合JavaBean特点的类可以当作普通类一样进行使用,但把它当avaBean用肯定需要带来一些额外的好处,我们才会去了解和应用JavaBean!好处如下:
Java EE开发中,经常要使用到JavaBean。很多环境就要求按JavaBean方式进行操作,别人都这么用和
要求这么做,那你就没什么挑选的余地!
部分代码:
ReflectPoint pt1 = new ReflectPoint(3,5);
//pt1对象中有一个x变量
String propertyName = "x";
//"x"-->"X"-->"getX"-->MethodGetX-->普通方法这样写会很麻烦
/*构造函数:PropertyDescriptor(String propertyName, Class<?> beanClass)
为符合标准 Java 约定的属性构造一个PropertyDescriptor。*/
//传两个参数,第一个是属性名,第二个是把那个对相当JavaBenn类
PropertyDescriptor pd1 = new PropertyDescriptor(propertyName,pt1);
//通过get或set方法获取操作那个变量的方法
Method method = pd1.getReadMethod();
//pt1调用这个函数,方法里面没有参数
Object boject = method.invoke(pt1);
总结:
通过PropertyDescriptor类,把要变成JavaBean的那个类和要操作的参数传进去,
然后再通过PropertyDescriptor类的set或get方法获取对象中操作那个变量的方法,
然后通过invoke让指定对象去操作这个方法。
对JavaBean进行内省操作
1. 通过 PropertyDescriptor 类操作那个JavaBean类
2. 采用遍历BeanInfo的所有属性方式来查找和设置某个RefectPoint对象的X属性,在程序
中把一个类当作JavaBean来看,就是调用IntroSpecto.getBeanInfo方法,
得到的BeanInfo对象封装了吧这个类当作JavaBean看的结果信息。
----------------------------------------------------------------------------
Beanutils工具包
commons-beanutils.jar在导入这个工具包的同时还要导入第三方的工具包
阿帕奇提供的日志开发包,叫:commons-logging.jar可以对Javabean进行操作
BeanUtils.getProperty()//得到JavaBean中的属性
BeanUtils.setProperty(pt1,"x","9");//设置
set里的X是int 但是我们用Beanutils设置值的时候是用String类型设置进去的,返回也是String类型的。
PropertiesUtils类
PropertiesUtils类里面也有getheset方法
PropertiesUtils.setProperty(pt1,"x",9);
Beanutils与PropertiesUtils的区别:
相关代码:
BeanUtils.setProperty(pt1, "birthday.time", "111");
System.out.println(BeanUtils.getProperty(pt1, "birthday.time"));
PropertyUtils.setProperty(pt1, "x", 9);
System.out.println(PropertyUtils.getProperty(pt1, "x").getClass().getName());
BeanUtils是以字符串的形式对javabean进行操作,
而PropertiesUtils是以属性本身的类型进行操作。
Beanutils可以与map相互转换
----------------------------------------------------------------------------
了解注解及java提供的几个基本注解 注解就是向编译器或者软件传达一些信息
@Override 表示当前方法是覆盖父类的方法。
@Deprecated 表示当前元素已过时。
@SuppressWarnings 表示关闭一些不当的编译器警告信息,就是不让你警告了你别管了。
元注解
@Target表示该注解用于什么地方,可能的 ElemenetType 参数包括:
ElemenetType.CONSTRUCTOR 构造器声明
ElemenetType.FIELD 域声明(包括 enum 实例)
ElemenetType.LOCAL_VARIABLE 局部变量声明
ElemenetType.METHOD 方法声明
ElemenetType.PACKAGE 包声明
ElemenetType.PARAMETER 参数声明
ElemenetType.TYPE 类,接口(包括注解类型)或enum声明
@Target(ElementType.METHOD)枚举此注解只能加在方法上
@Target({ElementType.METHOD,ElementType.TYPE})//也可以加到类上
@Retention 表示在什么级别保存该注解信息。可选的 RetentionPolicy 参数包括:
RetentionPolicy.SOURCE 注解将被编译器丢弃
RetentionPolicy.CLASS 注解在class文件中可用,但会被VM丢弃
RetentionPolicy.RUNTIME VM将在运行期也保留注释,因此可以通过反射机制读取注解的信息。
@Documented 将此注解包含在 javadoc 中
@Inherited 允许子类继承父类中的注解
总结:
注解相当于一种标记,加了注解就等于为程序打上了某种标记,
为注解增加基本属性
定义基本类型的属性和应用属性:
在注解类中增加String color();@MyAnnotation(color="red")
用反射方式获得注解对应的实例对象后,再通过该对象调用属性对应的方法
MyAnnotation a = (MyAnnotation)AnnotationTest.class.getAnnotation(MyAnnotation.class);
System.out.println(a.color());
可以认为上面这个@MyAnnotation是MyAnnotaion类的一个实例对象
为属性指定缺省值:
String color() default "yellow";
value属性:
String value() default "zxx";
如果注解中有一个名称为value的属性,且你只想设置value属性那么可以
省略value=部分,例如:@MyAnnotation("lhm")。
----------------------------------------------------------------------------
泛型
了解泛型
ArrayList<E>类定义和ArrayList<Integer>类引用中涉及如下术语:
整个称为ArrayList<E>泛型类型
ArrayList<E>中的E称为类型变量或类型参数
整个ArrayList<Integer>称为参数化的类型
ArrayList<Integer>中的Integer称为类型参数的实例或实际类型参数
ArrayList<Integer>中的<>念着typeof
ArrayList称为原始类型
泛型的好处:
1:将运行时期的问题ClassCastException问题转换成了编译失败,体现在编译时期,程序员就可以解决问题。
2:避免了强制转换的麻烦。
参数化类型与原始类型的兼容性:
参数化类型可以引用一个原始类型的对象,编译报告警告,
例如,Collection<String> c = new Vector();
原始类型可以引用一个参数化类型的对象,编译报告警告,
例如,Collection c = new Vector<String>();
----------------------------------------------------------------------------
个人认为:两边都指定的话要一样,两边任意一边指定的话也能兼容,但是不能两边都指定了而类
型不一样。另外参数化类型不考虑类型参数的继承关系:
Vector<String> v = new Vector<Object>(); //错误!///不写<Object>没错,写了就是明知故犯
Vector<Object> v = new Vector<String>(); //也错误!
编译器不允许创建泛型变量的数组。即在创建数组实例时,数组的元素不能使用参数化的类型,
例如,下面语句有错误:
Vector<Integer> vectorList[] = new Vector<Integer>[10];
----------------------------------------------------------------------------
泛型类:将泛型定义在类上。
class Tool<Q>
{
private Q obj;
public void setObject(Q obj)
{
this.obj = obj;
}
public Q getObject()
{
return obj;
}
}
用于放置泛型的类型参数的尖括号应出现在方法的其他所有修饰符之后和在方法的返回类型之前,
也就是紧邻返回值之前。按照惯例,类型参数通常用单个大写字母表示。
只有引用类型才能作为泛型方法的实际参数,swap(new int[3],3,5);语句会报告编译错误。
----------------------------------------------------------------------------
当方法操作的引用数据类型不确定的时候,可以将泛型定义在方法上。
static <E> void swap(E[] a, int i, int j)
{
E t = a[i];
a[i] = a[j];
a[j] = t;
}
静态方法上的泛型:静态方法无法访问类上定义的泛型。如果静态方法操作的引用数据类型不
确定的时候,必须要将泛型定义在方法上。
public static <Q> void function(Q t)
{
System.out.println("function:"+t);
}
注意:
在对泛型类型进行参数化时,类型参数的实例必须是引用类型,不能是基本类型。
当一个变量被声明为泛型时,只能被实例变量、方法和内部类调用,而不能被静态变量和静态方
法调用。因为静态成员是被所有参数化的类所共享的,所以静态成员不应该有类级别的类型参数
----------------------------------------------------------------------------
泛型接口.
interface Inter<T>
{
void show(T t);
}
class InterImpl<R> implements Inter<R>
{
public void show(R r)
{
System.out.println("show:"+r);
}
}
泛型中的?通配符
使用?通配符可以引用其他各种参数化的类型,?通配符定义的变量主要用作引用,可以调用与参
数化无关的方法,不能调用与参数化有关的方法。
定义一个方法,该方法用于打印出任意参数化类型的集合中的所有数据,该方法如何定义呢?
public static void printCollection(Collection<?> cols)
{
for(Object obj:cols)
{
System.out.println(obj);
}
//cols.add("string");//错误,因为它不知自己未来匹配就一定是String
cols.size();//没错,此方法与类型参数没有关系
cols = new HashSet<Date>();
}
泛型限定:
限定通配符的上边界:
正确:Vector<? extends Number> x = new Vector<Integer>();
错误:Vector<? extends Number> x = new Vector<String>();
限定通配符的下边界:
正确:Vector<? super Integer> x = new Vector<Number>();
错误:Vector<? super Integer> x = new Vector<Byte>();
上限:?extends E:可以接收E类型或者E的子类型对象。
下限:?super E:可以接收E类型或者E的父类型对象。
上限什么时候用:往集合中添加元素时,既可以添加E类型对象,又可以添加E的子类型对象。
为什么?因为取的时候,E类型既可以接收E类对象,又可以接收E的子类型对象。
下限什么时候用:当从集合中获取元素进行操作的时候,可以用当前元素的类型接收,也可以用当前元
素的父类型接收。
----------------------------------------------------------------------------
另外泛型只是在编译的时候给编译器看的,运行的时候已经去掉了类型的信息,所以可以通过反射
给一个个集合什么的添加其他的类型。
部分代码:
ArrayList<Integer> collection3 = new ArrayList<Integer>();
collection3.getClass().getMethod("add", Object.class).invoke(collection3,"abc");
System.out.println(str);
System.out.println(collection3.get(0));
泛型只能在编译的时候检查对应的类型,在运行的时候已经去掉了类型的信息。
泛型中的“?”通配符:
表示任意类型
1、泛型的参数类型只能是类(class)类型,而不能是简单类型。
比如,是不可使用的。
2、可以声明多个泛型参数类型,比如,同时还可以嵌套泛型,例如:>
3、泛型的参数类型可以使用extends语句,例如。
4、泛型的参数类型可以使用super语句,例如< T super childclass>。
5、泛型还可以使用通配符,例如<? extends ArrayList>
如果有一个函数中的参数用到了泛型,怎么获得泛型的类型呢?
获取函数名---泛型的方式获取函数里的参数类型----获取参数类型的实际类型
----------------------------------------------------------------------------
把map集合转成set的方法:
Set keySet();
Set entrySet();//取的是键和值的映射关系。
Entry就是Map接口中的内部接口;
为什么要定义在map内部呢?entry是访问键值关系的入口,是map的入口,访问的是map中的键值对。
取出map集合中所有元素的方式一:keySet()方法。
可以将map集合中的键都取出存放到set集合中。对set集合进行迭代。迭代完成,再通过get方法对
获取到的键进行值的获取。
Set keySet = map.keySet();
Iterator it = keySet.iterator();
while(it.hasNext())
{
Object key = it.next();
Object value = map.get(key);
System.out.println(key+":"+value);
}
----------------------------------------------------------------------------
取出map集合中所有元素的方式二:entrySet()方法。
HashMap maps = new HashMap();
maps.put("swk",18);
maps.put("zbj",20);
maps.put("shs",25);
Set<Map.Entry<String,Integer>> entrySet = maps.entrySet();
for(Map.Entry<String,Integer> entry : entrySet)
{
System.out.println(entry);
}
}
----------------------------------------------------------------------------
类加载器的委托机制:
自定义类加载器:
1,引导类加载器(bootstrap class loader):它用来加载 Java 的核心库,是用原生代码来实现的,并不继承自 java.lang.ClassLoader。运行JRE/lib/rt.jar里面的类
2,扩展类加载器(extensions class loader):它用来加载 Java 的扩展库。Java 虚拟机的实现会提供一个扩展库目录。该类加载器在此目录里面查找并加载 Java 类。这里面的JRE/lib/ext/ *.jar
3,系统类加载器(system class loader):它根据 Java 应用的类路径(CLASSPATH)来加载 Java 类。一般来说,Java 应用的类都是由它来完成加载的。可以通过ClassLoader.getSystemClassLoader() 来获取它。
类加载器的委托机制
当java中要加载一个类时,每个类加载器加载类时,先委托给其上级类加载器。就是父类先加载最后才轮到发起的的类加载器。 BootStrap--->ExtClassLoader--->AppClassLoader
自定义类加载器步骤:
1.编写一个对文件内容进行简单加密的程序。
2.编写了一个自己的类装载器,可实现对加密过的类进行装载和解密。
3.编写一个程序调用类加载器加载类,在源程序中不能用该类名定义引用变量,因为编译器无法识别这个类。程序中可以除了使用ClassLoader.load方法之外,还可以使用设置线程的上下文类加载器或者系统类加载器,然后再使用Class.forName。
----------------------------------------------------------------------------
代理类
一、代理的概念与作用
1.生活中的代理:
武汉众武汉的代理商手中买联想电脑和直接跑到北京传智播客旁边来找联想总部买电脑 你觉得最终的主体业务目标有什么区别吗?基本上一样吧,
都解决了核心问题,但是,一点区别都没有吗?从代理商那里买真的一点好处都没有吗?
2.程序中的代理:
要为已存在的多个具有相同接口的目标类的各个方法增加一些系统功能 。例如异常处理,日志,计算方法的运行时间 ,事务管理,等等
你准备如何做?
编写一个与目标类具有相同接口的代理类,代理类的每个方法调用目标类的相同方法,并在调用方法时加上系统功能的代码如果采用工厂模式和配置文件的方式进行管理,则不需要修改客户端程序,在配置文件中配置是使用目标类,还是代理类,这样以后很容易切换譬如,想要日志功能时就配置代理类,否则配置目标类,这样,增加系统功能很容易,以后运行一段时间后,又想去掉系统功能也很容易。
二、AOP
1、系统中存在交叉业务,一个交叉业务就是要切入到系统中的一个方面,如下所示:
安全 事务 日志
Student Service ----------|-----------|----------|----------
CourseService ----------|-----------|----------|----------
MiscService ------------|-----------|----------|----------
2、用具体的程序代码描述交叉业务:
method1 method2 method3
{ { {
------------------------------------------------切面
------ -------- ------
------------------------------------------------切面
} } }
3、交叉业务的编程问题即为面向方面的编程(Aspect oiented program,简称AOP),AOP的目标就是要使交叉业务模块化。可以采用将切面代码移动到原始方法的周围,这与直接在方法中编写切面代码的运行效果是一样的。如下所示:
--------------------------------------------------切面
func1 func2 func3
{ { {
--- --- ---
} } }
-------------------------------------------------切面
4、使用代理技术正好可以解决这种问题,代理是实现AOP功能的核心和关键技术。
三、动态代理技术:
1.要为系统中的各种接口的类增加代理功能,那将需要太多的代理类,全部采用静态代理方式,将是一件非常麻烦的事情.
2.JVM可以在运行期动态生成出类的字节码,这种动态生成的类往往被用作代理类,即动态代理类
3.JVM生成的动态类必须实现一个或多个接口,所以JVM生成的动态类只能用作具有相同接口的目标类的代理
4.CGLIB库可以动态生成一个类的子类,一个类的子类也可以用作该类的代理,所以,如果要为一个没有实现接口的类生成动态代理类,那么可以使用CGLIB库
5.代理类的各个方法中通常除了要调用目标的相应方法和对外返回目标返回的结果 ,还可以在代理方法中的如下四个位置加上系统功能代码 :
(1)在调用目标方法之前
(2)在调用目标方法之后
(3)在调用目标方法前后
(4)在处理目标方法异常的catch块中
----------------------------------------------------------------------------------
完成InvocationHandler对象的内部功能
一、分析JVM动态生成的类
1、创建实现类Collection接口的动态类和查看其名称,分析Proxy.getProxyClass方法的各个参数。
2、编码列出动态类中的所有构造方法和参数签名
3、编码列出动态类中的所有方法和参数签名
4、创建动态类的实例对象
(1)、用反射获得构造方法
(2)、编写一个最简单的InvocationHandler类
(3)、调用构造方法创建动态类的实例对象,并将编写的InvocationHandler类的实例对象传递进去
(4)、打印创建的对象和调用对象的没有返回值的方法和getClass方法,演示调用其他有返回值的方法报告了异常。
(5)、将创建动态类的实例对象的代理改成匿名内部类的形式编写,锻炼大家习惯匿名内部类。
5、总结思考:让jvm创建动态类及其实例对象,需要给它提供哪些信息?
三个方面:
1、生成的类中有哪些方法,通过让其他实现哪些接口的方式进行告知;
2、产生的类字节码必须有一个关联的类加载器对象;
3、生成的类中的方法的代码是怎样的,也得由我们提供,把我们的代码写在一个约定好了接口对象的方法中,把对象传递给它,它调 用我的方法,即相当于插入了我们的代码。提供执行代码的对象就是那个InvocationHandler对象的invoke方法中加一点代码,就可 以看到这些代码被调用运行了。
6、用Proxy.newInstance方法直接一步就创建出代理对象。
53_黑马程序员_张孝祥_Java基础加强_分析InvocationHandler对象的运行原理
一、猜想分析动态生成的类的内部代码
1、动态生成的类实现了Collection接口(可以实现若干接口),生成的类有Collection接口中的所有方法和一个如下接受InvocationHandler参数的构造方法。
2、构造方法接受一个InvocationHandler对象,接受对象了要干什么用呢?该方法内部的代码会是怎样的呢?
Class $Proxy0{
InvocationHandler handler;
public $Proxy0(InvocationHandler handler)
{
this.handler = handler;
}
}
3、实现的Collection接口中的各个方法的代码又是怎样的呢?InvocationHandler接口中定义的invoke方法接受的三个参数又是什么意思?
Client程序调用objProxy.add("abc")方法时,涉及三要素:objProxy对象、add方法、"abc"参数
Class Proxy${
add(Object object){
return handler.invoke(Object proxy,Method method,Object[] args);
}
}
4、分析先前但因动态类的实例对象时,结果为什么会是null呢?调用有基本类型
在调用代理对象的方法的时候,会将方法的参数传递给handler中的invoke方法,在invoke方法中我们会找到对应的目标来调用目标对象的invoke方法的并得到目标对象方法的返回值,依次会返回给handler中的invoke方法->最后返回给代理对象的方法。
5、注意:
在代理实例上的 java.lang.Object 中声明的 hashCode、equals 或 toString 方法的调用将按照与编码和指派接口方法调用相同的方式进行编码,并被指派到调用处理程序的 invoke 方法,如上所述。传递到 invoke 的 Method 对象的声明类是 java.lang.Object。代理类不重写从 java.lang.Object 继承的代理实例的其他公共方法,所以这些方法的调用行为与其对 java.lang.Object 实例的操作一样。
--------------------------------------------------------------------------
总结分析动态代理类的设计原理与结构
一、让动态生成的类成为目标类的代理
1、分析动态代理的工作原理图
(2)、为bind方法增加Advice参数。
----------------------------------------------------------------------------------------------------------
编写可生成代理和插入通告的通用方法
package com.itcast.day2; import java.lang.reflect.*; import java.util.*; public class ProxyTest { public static void main(String[] args) throws Exception{ Class classProxy1 = Proxy.getProxyClass(Collection.class.getClassLoader(), Collection.class); System.out.println("-----------begin constructors list---------------"); System.out.println(classProxy1.getName()); Constructor[] constructors = classProxy1.getConstructors(); for(Constructor constructor : constructors){ String name = constructor.getName(); StringBuilder sBuilder = new StringBuilder(); sBuilder.append("("); Class[] clazzParams = constructor.getParameterTypes(); for(Class clazzParam : clazzParams){ sBuilder.append(clazzParam.getName()).append(","); } if(clazzParams!=null&&clazzParams.length!=0){ sBuilder.deleteCharAt(sBuilder.length()-1); } sBuilder.append(")"); System.out.println(sBuilder.toString()); } System.out.println("-----------begin methods list---------------"); Method[] methods = classProxy1.getMethods(); for(Method method : methods){ String name = method.getName(); StringBuilder sBuilder = new StringBuilder(name); sBuilder.append("("); Class[] clazzParams = method.getParameterTypes(); for(Class clazzParam : clazzParams){ sBuilder.append(clazzParam.getName()).append(","); } if(clazzParams!=null&&clazzParams.length!=0){ sBuilder.deleteCharAt(sBuilder.length()-1); } sBuilder.append(")"); System.out.println(sBuilder.toString()); } System.out.println("-----------begin create instance---------------"); Constructor constructor = classProxy1.getConstructor(InvocationHandler.class); //一般使用匿名内部类 class MyInvocationHander1 implements InvocationHandler{ public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { return null; } } //第一种 Collection proxy1 = (Collection)constructor.newInstance(new MyInvocationHander1()); //第二种 Collection proxy2 = (Collection)constructor.newInstance(new InvocationHandler(){ public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { return null; }}); //第三种 final ArrayList target = new ArrayList(); Collection proxy3 = (Collection)getProxy(target,new MyAdvice()); System.out.println(proxy3.toString()); System.out.println(proxy3.size()); proxy3.add("abc"); System.out.println(proxy3.size()); proxy3.clear(); System.out.println(proxy3.getClass().getName());//返回的是$Proxy0,不是ArrayList,只有hashCode equals toString 才委托给handler } private static Object getProxy(final Object target,final Advice advice) { Object proxy3 = Proxy.newProxyInstance( target.getClass().getClassLoader(), //Collection.class.getClassLoader(),这里也不能写collection target.getClass().getInterfaces(), //new Class[]{Collection.class}, 这里就不能写collection了 new InvocationHandler(){ //ArrayList target = new ArrayList();抽取出来 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { /* long beginTime = System.currentTimeMillis(); System.out.println(args); Object retVal = method.invoke(target, args); long endTime = System.currentTimeMillis(); System.out.println(method.getName()+" running time of "+(endTime-beginTime)); //return method.invoke(target, args); return retVal;*/ //long beginTime = System.currentTimeMillis(); advice.beforeMethod(method); Object retVal = method.invoke(target, args); advice.afterMethod(method); /* long endTime = System.currentTimeMillis(); System.out.println(method.getName()+" running time of "+(endTime-beginTime));*/ //return method.invoke(target, args); return retVal; }} ); return proxy3; } } ----------------------------------------- package com.itcast.day2; import java.lang.reflect.*; public interface Advice { public void beforeMethod(Method method); public void afterMethod(Method method); } ----------------------------------------- package com.itcast.day2; import java.lang.reflect.*; public class MyAdvice implements Advice { private long beginTime = 0; @Override public void afterMethod(Method method) { System.out.println("欢迎到从传智播客毕业上班"); long endTime = System.currentTimeMillis(); System.out.println(method.getName()+" running time of "+(endTime-beginTime)); } @Override public void beforeMethod(Method method) { System.out.println("欢迎到传智播客学习"); long beginTime = System.currentTimeMillis(); } }
------------------------------------------------------------------------------
实现类似spring的可配置的AOP框架
一、实现AOP功能的封装与配置
1、工厂类BeanFactory负责创建目标类或代理类的实例对象,并通过配置文件实现切换。其getBean方法根据参数字符串返回一个相应的实例对象,如果参数字符串在配置文件中对应的类名不是ProxyFactoryBean,则直接返回该类的实例对象,否则,返回该类实例对象的getProxy方法返回的对象。
2、BeanFactory的构造方法接收代表配置文件的输入流对象,配置文件格式如下:
#xxx=java.util.ArrayList
xxx=lqq.heima.day3.aopframework.ProxyFactoryBean
xxx.target=java.util.ArrayList
xxx.advice=lqq.heima.day3.MyAdvice
3、ProxyFactoryBean充当封装生成动态代理的工厂,需要为工厂类提供哪些配置参数信息?
目标
通知
4、编写客户端应用:
编写实现Advice接口的类和在配置文件中进行配置
调用BeanFactory获取对象。
package com.itcast.day2.aopframework; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; import com.itcast.day2.Advice; public class ProxyFactoryBean { private Advice advice; private Object target; public Object getProxy() { final Object target = getTarget(); final Advice advice = getAdvice(); Object proxy = Proxy.newProxyInstance( target.getClass().getClassLoader(), target.getClass().getInterfaces(), new InvocationHandler(){ public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { advice.beforeMethod(method); Object retVal = method.invoke(target, args); advice.afterMethod(method); return retVal; } } ); return proxy; } public Advice getAdvice() { return advice; } public void setAdvice(Advice advice) { this.advice = advice; } public Object getTarget() { return target; } public void setTarget(Object target) { this.target = target; } } //---------------------------------------------------- package com.itcast.day2.aopframework; import java.io.IOException; import java.io.InputStream; import java.util.Properties; import com.itcast.day2.Advice; public class BeanFactory { Properties props = new Properties(); public BeanFactory(InputStream ips){ try { props.load(ips); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } public Object getBean(String name){ String className = props.getProperty(name); Object bean = null; try { Class clazz = Class.forName(className); bean = clazz.newInstance(); } catch (Exception e) { // TODO: handle exception e.printStackTrace(); } if(bean instanceof ProxyFactoryBean){ Object proxy = null; ProxyFactoryBean proxyFactoryBean = (ProxyFactoryBean)bean; try { Advice advice = (Advice)Class.forName(props.getProperty(name+".advice")).newInstance(); Object target = Class.forName(props.getProperty(name+".target")).newInstance(); System.out.println(Class.forName(props.getProperty(name+".target")).getName()); proxyFactoryBean.setAdvice(advice); proxyFactoryBean.setTarget(target); proxy = proxyFactoryBean.getProxy(); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } return proxy; } return bean; } } //---------------------------------------------------- package com.itcast.day2; import java.lang.reflect.*; public interface Advice { public void beforeMethod(Method method); public void afterMethod(Method method); } //---------------------------------------------------- package com.itcast.day2; import java.lang.reflect.*; public class MyAdvice implements Advice { private long beginTime = 0; @Override public void afterMethod(Method method) { System.out.println("欢迎到从传智播客毕业上班"); long endTime = System.currentTimeMillis(); System.out.println(method.getName()+" running time of "+(endTime-beginTime)); } @Override public void beforeMethod(Method method) { System.out.println("欢迎到传智播客学习"); beginTime = System.currentTimeMillis(); } } //---------------------------------------------------- #xxx=java.util.ArrayList xxx=com.itcast.day2.aopframework.ProxyFactoryBean xxx.target=java.util.ArrayList xxx.advice=com.itcast.day2.MyAdvice
本文出自 “离歌丶D” 博客,转载请与作者联系!
原文地址:http://optimisticpig.blog.51cto.com/8445719/1410624