黑马程序员 java高新技术<二>--java5的枚举、反射的深入讲解
来源:互联网 发布:4g网络架构 编辑:程序博客网 时间:2024/06/05 07:04
-------android培训、java培训、java学习型技术博客、期待与您交流! ----------
知识点三 java5的枚举
一、概述:
jdk1.5增加的新特性,本来是去掉C中的枚举的,jdk1.5后又把很多从c语言中去掉的东西找了回来。
枚举就是一个类,每一个枚举元素都是一个对象。
1)为什么要有枚举
问题:要定义星期几或性别的变量,该怎么定义?假设用1-7分别表示星期一到星期日,但有人可能会写成int weekday = 0;或即使使用常量方式也无法阻止意外。
枚举就是要让某个类型的变量的取值只能为若干个固定值中的一个,否则,编译器就会报错。枚举可以让编译器在编译时就可以控制源程序中填写的非法值,普通变量的方式在开发阶段无法实现这一目标。
2)用普通类如何实现枚举功能,定义一个Weekday的类来模拟枚举功能。
私有的构造方法
每个元素分别用一个公有的静态成员变量表示
可以有若干公有方法或抽象方法。采用抽象方法定义nextDay就将大量的if.else语句转移成了一个个独立的类。
3)枚举的基本应用
举例:定义一个Weekday的枚举。
扩展:枚举类的values,valueOf,name,toString,ordinal等方法(记住,讲课时要先于自定义方法前介绍,讲课更流畅)
总结:枚举是一种特殊的类,其中的每个元素都是该类的一个实例对象,例如可以调用WeekDay.SUN.getClass().getName和WeekDay.class.getName()。
分析:如果想在一个类中编写完各/*英文,简写,中文
Monday, Mon 星期一
Tuesday, Tue 星期二
Wednesday, Wed 星期三
Thursday, Thu 星期四
Friday, Fri 星期五
Saturday, Sat 星期六
Sunday, Sun 星期日*/
用鼠标移动,按下,弹起等常量的值无法记忆来说明常量的好处。用下拉列表框来显示星期几,就引出了values()方法,用服务器接收的是”MON”字符串,要变成WeekDay.MON对象,就引出了valueOf()方法。
为了大家更好地理解枚举,先用普通类来模仿一下枚举,这可以参照java.awt.Color类。
枚举类的实例对象个数是有限的,就是那些成员,可以在枚举类的构造方法中加入监控语句,看到这几个实例对象被创建出来的过程。
如果想在一个类中编写完各个枚举类和测试调用类,那么可以将枚举类定义成调用类的内部类。
个枚举类和测试调用类,那么可以将枚举类定义成调用类的内部类。
示例:/*
注意 */package cn.itcast.day1;//枚举类public abstract class WeekDay1 { //把构造方法定义为私有的private WeekDay1(){}//final static这两个关键词哪个放在前面都可以//加上{}表示,我们必须用这个抽象类的子类来创建实例对象,这个子类没有名字,称为匿名类//这个子类的具体代码见这对大括号//把nextDay()方法分别由每个子类去实现。public final static WeekDay1 SUN=new WeekDay1(){@Overridepublic WeekDay1 nextDay() {// TODO Auto-generated method stubreturn MON;}};public final static WeekDay1 MON=new WeekDay1(){@Overridepublic WeekDay1 nextDay() {// TODO Auto-generated method stubreturn SUN;}};//让每个元素自己写自己的nextDay()方法public abstract WeekDay1 nextDay();//ctrl+shift+/注释代码/*public WeekDay nextDay(){if(this == SUN){return MON;}else{return SUN;}}*///打印每个枚举的值public String toString(){ //这里应该用if-else判断,这是简写的方法//return this==SUN?"SUN":"MON";if(this==SUN){return "SUN";}else{return "MON";}}}
package cn.itcast.day1;import java.util.Date;//Enumeration在集合中是迭代器//内部类可以用四个访问修饰符修饰,外部类只能是默认不写或者是public修饰的//因为内部类和成员方法和成员变量是平级的public class EnumTest {/** * @param args */public static void main(String[] args) {// TODO Auto-generated method stub//定义类型WeekDay,那么以后用这个类型定义的值就是这个类型规定范围的值,不是这些值得话,编译器就不能编过。WeekDay1 weekDay = WeekDay1.MON;System.out.println(weekDay.nextDay());//FRI现在是一个对象,枚举就相当于一个类,枚举当中的一个元素就相当这个类的一个实例对象WeekDay weekday2=WeekDay.FRI;//枚举自动的帮我们实现了toString()方法System.out.println(weekday2);//打印出自己的名字System.out.println(weekday2.name());//ordinal()自己的排行System.out.println(weekday2.ordinal());//getClass()的到自己的类System.out.println(weekday2.getClass());//将传来的参数变成WeekDay的实例对象,把一个串变成对应的枚举元素。System.out.println(WeekDay.valueOf("SUN").toString());//我们不能直接遍历枚举类,把枚举中的元素装入数组中,在对枚举中的数组进行遍历System.out.println(WeekDay.values().length);} //定义枚举所使用的关键字是enum//只要用到了类,它的静态变量都能执行public enum WeekDay{//元素列表一定要放在最前面//在枚举元素的后面跟上一对大括号()就表示调用带参数的构造方法SUN(1),MON(),TUE,WED,THI,FRI,SAT;//定义构造方法private WeekDay(){System.out.println("first");}private WeekDay(int day){System.out.println("second");//这样就表示New Date类的一个子类,{}直接可以写之子类的代码//子类的构造方法又是默认调用父类无参数的构造方法,当然可以调用父类有参的构造方法new Date(300){};}//实现带有抽象方法的枚举(枚举就是一个类,枚举的成员就是这个类的实例对象)public enum TrafficLamp{//定义三个子类,必须完成父类的 抽象方法RED(30){@Overridepublic TrafficLamp nextLamp() {// TODO Auto-generated method stubreturn GREEN;}},GREEN(45){@Overridepublic TrafficLamp nextLamp() {// TODO Auto-generated method stubreturn YELLOW;}},YELLOW(5){@Overridepublic TrafficLamp nextLamp() {// TODO Auto-generated method stubreturn RED;}};//定义一个抽象方法,nextLamp下一个灯public abstract TrafficLamp nextLamp();private int time;private TrafficLamp(int time){//把传进来的值赋值给我的成员变量this.time=time;}}}}二、枚举的高级应用:
1.枚举就相当于一个类,其中也可以定义构造方法、成员变量、普通方法和抽象方法。
2.枚举元素必须位于枚举体中的最开始部分,枚举元素列表的后要有分号与其他成员分隔。把枚举中的成员方法或变量等放在枚举元素的前面,编译器报告错误。
3.带构造方法的枚举
构造方法必须定义成私有的
如果有多个构造方法,该如何选择哪个构造方法?
枚举元素MON和MON()的效果一样,都是调用默认的构造方法。
4.带方法的枚举
定义枚举TrafficLamp
实现普通的next方法
实现抽象的next方法:每个元素分别是由枚举类的子类来生成的实例对象,这些子类采用类似内部类的方式进行定义。
增加上表示时间的构造方法
5.枚举只有一个成员时,就可以作为一种单例的实现方式。
补充:
写带有抽象方法的枚举步骤和技巧:
1. enum TrafficLamp{RED,GREEN,YELLOW}
2. enum TrafficLamp{RED,GREEN,YELLOW;public abstract next();}
3.enum TrafficLamp{RED{},GREEN{},YELLOW{};public abstract next();}
4.填充各个{}中的next方法
三、枚举中涉及的几个知识点
1、构造器:
1)构造器只是在构造枚举值的时候被调用。
2)构造器只有私有private,绝不允许有public构造器。这样可以保证外部代码无法重新构造枚举类的实例,这样是合情合理的,因为枚举值是public static final的常量,但是枚举类的方法和数据域是可以被外部访问的。
3)构造器可以有多个,调用哪个即初始化相应的值。
2、所有的枚举类都继承了Enum方法,方法如下:
1)toString() ---> 返回枚举量的名称
2)ordina() ---> 返回枚举值在枚举类中的顺序,按定义的顺序排
3)getClass() ---> 获取对应的类名
静态方法:
1)valueOf(String e) ---> 转为类中对应的对象,即将字符串转为对象
2)values() ---> 获取数组,将元素放入一个数组中。
四、注意:
1、元素列表即所有的元素项目,必须放在整个类的最前面。
2、元素列表后要有分号
3、枚举类中的方法为private
4、在元素后跟上(),就可指定要使用的构造方法,含参数。
5、枚举类中的每个元素都是它的一个子类对象,当枚举类中存在abstract方法,须在子类后加{},并在其中实现此方法。
五、总结:
1、匿名内部类比较常用
2、类的方法返回的类型可以是类的类型
3、类中可定义静态常量,常量的结果就是自己这个类型的实例对象
4、如果枚举只有一个成员,就可以作为一种单利的实现方式,对象只有一个。
人->Person
Java类->Class
2)Class类代表Java类,它的各个实例对象又分别对应什么呢?
对应各个类在内存中的字节码,例如,Person类的字节码,ArrayList类的字节码,等等。
一个类被类加载器加载到内存中,占用一片存储空间,这个空间里面的内容就是类的字节码,不同的类的字节码是不同的,所以它们在内存中的内容是不同的,这一个个的空间可分别用一个个的对象来表示,这些对象显然具有相同的类型,这个类型是什么呢?
①、Class是Java程序中各个Java类的总称;它是反射的基石,通过Class类来使用反射。
②、Class和class的区别
class:Java中的类用于描述一类事物的共性,该类事物有什么属性,没有什么属性,至于这个属性的值是什么,则由此类的实例对象确定,不同的实例对象有不同的属性值。
Class:指的是Java程序中的各个Java类是属于同一类事物,都是Java程序的类,这些类称为Class。例如人对应的是Person类,Java类对应的就是Class。
3)如何得到各个字节码对应的实例对象( Class类型)
类名.class,例如,System.class
对象.getClass(),例如,new Date().getClass()
Class.forName("类名"),例如,Class.forName("java.util.Date");
当获取类名的时候,是不知道此类的名称的,forName(字符串参数)方法中传入字符串型的变量作为对外访问的入口,即传入什么类名就获得什么类名,从而得知相应的类名。
注:
forName()是静态方法,是反射中使用的一种方式获取字节码的实例对象。
每个类的字节码对象只有唯一的一个,如任何字符串对象,对应唯一的String.clas字节码。
注意:对象的创建和使用:
①、创建实例对象:不可用new Class()的方式,因为Class没有这样的构造方法。而是将字节码对象赋值给Class变量。如Class c1 =Person.class。
如Person类,它的字节码:首先要将Person的java文件编译为class文件放于硬盘上,即为二进制代码,再将这些代码加载到内存中,接着用它创建一个个对象。就是把类的字节码加载进内存中,再用此字节码创建一个个对象。当有如Person、Math、Date等等的类,那么这些字节码就是分别的一个Class对象。即Class c2 =Date.class;。
②、获得类的字节码对象:如Class.forName(”java.lang.String”)即获得String.class。得到这个字节码对象有两种情况:
此类已经加载进内存:若要得到此类字节码,不需要再加载。
此类还未加载进内存:类加载器加载此类后,将字节码缓存起来,forName()方法返回加载进来的字节码。
4)九个预定义Class实例对象:参看Class.isPrimitive方法的帮助
5)数组类型的Class实例对象
Class.isArray()
总之,只要是在源程序中出现的类型,都有各自的Class实例对象,例如,int[].class、void.class等。
6)方法:
1、static Class forName(String className)
---> 返回与给定字符串名的类或接口的相关联的Class对象。
2、Class getClass()
---> 返回的是Object运行时的类,即返回Class对象即字节码对象
3、Constructor getConstructor()
---> 返回Constructor对象,它反映此Class对象所表示的类的指定公共构造方法。
4、Field getField(String name)
---> 返回一个Field对象,它表示此Class对象所代表的类或接口的指定公共成员字段。
5、Field[] getFields()
---> 返回包含某些Field对象的数组,表示所代表类中的成员字段。
6、Method getMethod(String name,Class… parameterTypes)
---> 返回一个Method对象,它表示的是此Class对象所代表的类的指定公共成员方法。
7、Method[] getMehtods()
返回一个包含某些Method对象的数组,是所代表的的类中的公共成员方法。
8、String getName()
---> 以String形式返回此Class对象所表示的实体名称。
9、String getSuperclass()
---> 返回此Class所表示的类的超类的名称
10、boolean isArray()
---> 判定此Class对象是否表示一个数组
11、boolean isPrimitive()
---> 判断指定的Class对象是否是一个基本类型。
12、T newInstance()
---> 创建此Class对象所表示的类的一个新实例。
示例:
public static void fuction()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);//trueSystem.out.println(cls1 == cls3);//true//判断是否为基本类型:isPrimitive()System.out.println(cls1.isPrimitive());//falseSystem.out.println(int.class == Integer.class);//false//Integer.TYPE代表包装类对应的基本数据类型的字节码System.out.println(int.class == Integer.TYPE);//trueSystem.out.println(int[].class.isPrimitive());//false//判断是否为数组类型的System.out.println(int[].class.isArray());//true}
二、深入理解反射的概念
1.反射就是把Java类中的各种成分映射成相应的java类。
2.一个Java类中用一个Class类的对象来表示,一个类中的组成部分:成员变量,方法,构造方法,包等等信息也用一个个的Java类来表示,就像汽车是一个类,汽车中的发动机,变速箱等等也是一个个的类。表示java类的Class类显然要提供一系列的方法,来获得其中的变量,方法,构造方法,修饰符,包等信息,这些信息就是用相应类的实例对象来表示,它们是Field、Method、Contructor、Package等等。
3.一个类中的每个成员都可以用相应的反射API类的一个实例对象来表示,通过调用Class类的方法可以得到这些实例对象后,得到这些实例对象后有什么用呢?怎么用呢?这正是学习和应用反射的要点。
2)得到某个类所有的构造方法:
例子:Constructor [] constructors= Class.forName("java.lang.String").getConstructors();
3)得到某一个构造方法:
例子:
//获得方法时要用到类型
4)创建实例对象:
通常方式:String str = new String(new StringBuffer("abc"));
反射方式: String str = (String)constructor.newInstance(new StringBuffer("abc"));②
//调用获得的方法时要用到上面相同类型的实例对象
注意:上面的两个地方①②都要用到StringBuffer,这必须是一致的。
第①个是指定要带StringBuffer参数类型的构造方法,即所需使用的是含StringBuffer类型的构造方法。
第②个是用这个构造方法创建对象,要传入的参数类型是StringBuffer。
5)Class.newInstance()方法:例子:String obj = (String)Class.forName("java.lang.String").newInstance();
该方法内部先得到默认的无参数的构造方法,然后用该构造方法创建实例对象。
该方法内部的具体代码是怎样写的呢?用到了缓存机制来保存默认构造方法的实例对象。
public int y;
public ReflectPoint(int x, int y) {
super();
this.x = x;
this.y = y;
}
@Override
public String toString(){
return str1 + ":" + str2 + ":" + str3;
}
}
public void setX(int x) {
this.x = x;
}
public int getY() {
return y;
}
public void setY(int y) {
this.y = y;
}
}
ReflectPoint pt1 = new ReflectPoint(3,5);
//fieldX和fieldY并不是对象身上的变量,而是类上的
//要用它去取某个对象上的对应的值,传入什么对象,就取相应对象的值。
Field fieldY = pt1.getClass().getField("y");
System.out.println(fieldY.get(pt1));
//获取私有的成员变量
Field fieldX = pt1.getClass().getDeclaredField("x");
fieldX.setAccessible(true);
System.out.println(fieldX.get(pt1));
}
3)问题:得到的Field对象是对应到类上面的成员变量,还是对应到对象上的成员变量?类只有一个,而该类的实例对象有多个,如果是与对象关联,哪关联的是哪个对象呢?所以字段fieldX 代表的是x的定义,而不是具体的x变量。
示例代码:
ReflectPoint point = new ReflectPoint(1,7);
Field y = Class.forName("cn.itcast.corejava.ReflectPoint").getField("y");
System.out.println(y.get(point));
//Field x = Class.forName("cn.itcast.corejava.ReflectPoint").getField("x");
Field x = Class.forName("cn.itcast.corejava.ReflectPoint").getDeclaredField("x");
x.setAccessible(true);
System.out.println(x.get(point));
1)获取公有的成员变量:
getField(String name)和get(变量)
2)获取私有的成员变量:暴力反射
getDeclared(String name)
setAccessible(boolean b),将b设为true即可
get(变量)
举例:
//替换字符
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);
}
}
}
5)作业:将任意一个对象中的所有String类型的成员变量所对应的字符串内容中的"b"改成"a"。
{
String name="abc";
String email="abd";
int x = 5;
}
func(Object obj)
{
Field [] fields = obj.getClass().getDeclaredFields();
for(Field field : fields)
{
if(field.getType()==java.lang.String.class)
{
field.setAccesible(true);
String original = (String)field.get(obj);
field.set(obj,original.replaceAll("b","a");
}
}
}
2)得到类中的某一个方法:
例子: Method charAt = Class.forName("java.lang.String").getMethod("charAt", int.class);
3)调用方法:
通常方式:System.out.println(str.charAt(1));
反射方式: System.out.println(charAt.invoke(str, 1));
如果传递给Method对象的invoke()方法的第一个参数为null,这有着什么样的意义呢?说明该Method对象对应的是一个静态方法!
4)jdk1.4和jdk1.5的invoke方法的区别:
Jdk1.5:public Object invoke(Object obj,Object... args)
Jdk1.4:public Object invoke(Object obj,Object[] args),即按jdk1.4的语法,需要将一个数组作为参数传递给invoke方法时,数组中的每个元素分别对应被调用方法中的一个参数,所以,调用charAt方法的代码也可以用Jdk1.4改写为 charAt.invoke(“str”, new Object[]{1})形式。
专家模式:谁调用这个数据,就是谁在调用它的专家。
如人关门:
调用者:是门调用管的动作,对象是门,因为门知道如何执行关的动作,通过门轴之类的细节实现。
指挥者:是人在指挥门做关的动作,只是给门发出了关的信号,让门执行。
总结:变量使用方法,是方法本身知道如何实现执行的过程,也就是“方法对象”调用方法,才执行了方法的每个细节的。
6)用反射方式执行某个类中的main方法
原理:
首先要明确为何要用反射:在写源程序时,并不知道使用者传入的类名是什么,但是虽然传入的类名不知道,而知道的是这个类中的方法有main这个方法,所以可以通过反射的方式,通过使用者传入的类名(可定义字符串型变量作为传入类名的入口,通过这个变量代表类名),内部通过传入的类名获取其main方法,然后执行相应的内容。
1)目标:
写一个程序,这个程序能够根据用户提供的类名,去执行该类中的main方法。用普通方式调完后,大家要明白为什么要用反射方式去调啊?
2)问题:
启动Java程序的main方法的参数是一个字符串数组,即public static void main(String[] args),通过反射方式来调用这个main方法时,如何为invoke方法传递参数呢?按jdk1.5的语法,整个数组是一个参数,而按jdk1.4的语法,数组中的每个元素对应一个参数,当把一个字符串数组作为参数传递给invoke方法时,javac会到底按照哪种语法进行处理呢?jdk1.5肯定要兼容jdk1.4的语法,会按jdk1.4的语法进行处理,即把数组打散成为若干个单独的参数。所以,在给main方法传递参数时,不能使用代码mainMethod.invoke(null,new String[]{“xxx”}),javac只把它当作jdk1.4的语法进行理解,而不把它当作jdk1.5的语法解释,因此会出现参数类型不对的问题。
3)解决办法:
mainMethod.invoke(null,new Object[]{new String[]{"xxx"}});
mainMethod.invoke(null,(Object)new String[]{"xxx"}); ,编译器会作特殊处理,编译时不把参数当作数组看待,也就不会数组打散成若干个参数了。
示例:
//Method类演示 private static void methodTest(String [] args) throws Exception { String str1 = "abc"; //一般方法: System.out.println(str1.charAt(1)); //反射方法 : Method methodCharAt = Class.forName("java.lang.String").getMethod("charAt",int.class); System.out.println(methodCharAt.invoke(str1,1)); //用反射方式执行某个main方法 //一般方式: Test.main(new String[]{"111","222","333"}); System.out.println("-------"); //反射方式: String startingClassName = args[0]; Method methodMain = Class.forName(startingClassName).getMethod("main",String[].class); //方案一:强制转换为超类Object,不用拆包 methodMain.invoke(null,(Object)new String[]{"111","222","333"}); //方案二:将数组打包,编译器拆包后就是一个String[]类型的整体 methodMain.invoke(null,new Object[]{new String[]{"111","222","333"}}); } //定义一个测试类 class Test{ public static void main(String [] args){ for(String arg : args){ System.out.println(arg); } } }
四、数组的反射
1、数组字节码的名字:有[和数组对应类型的缩写,如int[]数组的名称为:[I
2、基本数据类型的一维数组不能转换为Object数组,如:
int[] a = new int[3];Object[] obj= a;这样是不成立的。
3、如何得到某个数组中的某个元素的类型:
例:int a = newint[3];Object[] obj= new Object[]{”ABC”,1};
无法得到某个数组的具体类型,只能得到其中某个元素的类型,如
Obj[0].getClass().getName()得到的是java.lang.String
若通过b.getClass().getName(),结果是:[Ljava.lang.Object;
4.具有相同维数和元素类型的数组属于同一个类型,即具有相同的Class实例对象。
5.代表数组的Class实例对象的getSuperClass()方法返回的父类为Object类对应的Class。
6.基本类型的一维数组可以被当作Object类型使用,不能当作Object[]类型使用;非基本类型的一维数组,既可以当做Object类型使用,又可以当做Object[]类型使用。
7.Arrays.asList()方法处理int[]和String[]时的差异。
Array工具类用于完成对数组的反射操作。
补充:
Object[] 与String[]没有父子关系,Object与String有父子关系,所以new Object[]{“aaa”,”bb”}不能强制转换成new String[]{“aaa”,”bb”};,Object x = “abc”能强制转换成String x = “abc”。
main.invoke(null, (Object)(new Object[]{“aaa”,“xxx”}));不能调用public static void main(String [] args)
1.
看Class类帮助文档开始部分的介绍。
String[] a1 = new String[8];
String[] a2 = new String[9];
String[][] a3 = new String[6][4];
().getName());
System.out.println(a3.getClass().getName());
System.out.println(a1.getClass() == a2.getClass());
System.out.println(a1.getClass() == a3.getClass());
2.
看getSuperClass方法的帮助
System.out.println(a1.getClass().getSuperclass().getName());
3.
int[] a = new int[3];
Object obj = a;
//Object[] obj1 = a //有错!
Object[] obj3 = a1
Object obj4 = a3;
if(obj4 instanceof String[][]){
System.out.println(obj1.getClass().isArray());
}
**在这里分析研究Arrays.asList()方法处理int[]和String[]时的差异,以及Arrays.deepToString()方法不能处理int[],但能处理String[]的原因。
4.
private static void printObject(Object obj) {
if(obj.getClass().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);
}
}
5.似乎没有办法直接得到,需要取出每个元素对象,然后再对各个对象进行判断,因为其中每个具体元素的类型都可以不同,例如Object[] x = new Object[]{“abc”,Integer.Max}。Object[] 与String[]没有父子关系,Object与String有父子关系,所以new Object[]{“aaa”,”bb”}不能强制转换成new String[]{“aaa”,”bb”};,Object x = “abc”能强制转换成String x = “abc”。
main.invoke(null, (Object)(new Object[]{“aaa”,“xxx”}));不能调用public static void main(String [] args)
示例:
public static void arrayTest()throws Exception { int[] a1 = new int[]{1,2,3}; int[] a2 = new int[4]; int[][] a3 = new int[2][3]; Integer[] ai = new Integer[3]; String[] a4 = new String[]{"a","b","c"}; System.out.println(a1.getClass() == a2.getClass()); System.out.println((Object)a1.getClass() == (Object)a3.getClass()); System.out.println(a3[0].getClass() == a1.getClass()); System.out.println(a3[0].getClass().getSuperclass().getName()); System.out.println(a1.getClass().equals(a3.getClass())); System.out.println(a1.getClass().equals(a4.getClass())); System.out.println(a1.getClass().getName()); System.out.println("----:" + a1.getClass()); System.out.println(a1.getClass().getSuperclass().getName()); System.out.println(a2.getClass().getSuperclass().getName()); Object obj1 = a1; Object obj2 = a2; //int基本数据类型不是Object的 //Object[] obj3 = a1; Object[] obj4 = a3; Object[] obj5 = a4; System.out.println(a1); System.out.println(a4); System.out.println(Arrays.asList(a1)); System.out.println(Arrays.asList(a4)); System.out.println("-------"); int[] a = new int[3]; Object[] obj = new Object[]{"abc",new Integer(1)}; System.out.println(a.getClass().getName()); System.out.println(obj.getClass().getName()); System.out.println(obj[0].getClass().getName()); }
四、
ArrayList_HashSet的比较及Hashcode分析
覆写hashCode()方法的意义:只有存入的是具有hashCode算法的集合的,覆写hashCode()方法才有价值。
1、哈希算法的由来:
若在一个集合中查找是否含有某个对象,通常是一个个的去比较,找到后还要进行equals的比较,对象特别多时,效率很低,通过哈希算法,将集合分为若干个区域,每个对象算出一个哈希值,可将哈希值分组(一般模32为一组),每组对应某个存储区域,依一个对象的哈希码即可确定此对象对应区域,从而减少每个对象的比较,只需在指定区域查找即可,从而提高从集合中查找元素的效率。
示意图:2、如果不存入是hashCode算法的集合中,那么则不用复写此方法。
3、只有类的实例对象要被采用哈希算法进行存入和检索时,这个类才需要按要求复写hashCode()方法,即使程序可能暂时不会用到当前类的hashCode()方法,但是为提供一个hashCode()方法也不会有什么不好,没准以后什么时候就会用到这个方法,所以通常要求hashCode()和equals()两者一并被覆盖。
4、提示:
1)若同类两对象用equals()方法比较的结果相同时,他们的哈希码也必须是相等的,但反过来就不成立了,如”BB”和”Aa”两字符串用equals()比较式不相等的,但是他们的哈希值是相等的。
2)当一个对象被存储进HashSet集合中,就不能再修改参与计算哈希值的字段,否则对象被修改后的哈希值与最初被存入的HashSet集合中的哈希值就不同了。在这种情况下,即使contains()方法是用对象的当前引用作为参数去HashSet集合中检索对象,也将返回找不到对象的结果,这也导致无法从HashSet集合中单独删除当前对象,从而造成内存泄露。
简单说,之前存入的对象和修改后的对象,是具有不同的哈希值,被认为是不同的两个对象,这样的对象不再使用,又不移除,而越来越多,就会导致内存泄露。
补充:
内存泄露:某些对象不再使用了,占用着内存空间,并未被释放,就会导致内存泄露;也就是说当程序不断增加对象,修改对象,删除对象,日积月累,内存就会用光了,就导致内存溢出。
3)对象在调用方法时,对象会先进行一次自身hashCode()方法的调用,再进行操作方法。
示例:
import java.util.ArrayList; import java.util.Collection; import java.util.HashSet; public class ReflectTest2 { public static void main(String [] args){ Collection cons = new HashSet(); ReflectPoint pt1 = new ReflectPoint(3,3); ReflectPoint pt2 = new ReflectPoint(5,5); ReflectPoint pt3 = new ReflectPoint(3,3); cons.add(pt1); cons.add(pt2); cons.add(pt3); cons.add(pt1); cons.remove(pt1); System.out.println(cons.size()); } } public 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 int hashCode() { final int prime = 31; int result = 1; result = prime * result + x; result = prime * result + y; //System.out.println("demo...");//测试 return result; } public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; ReflectPoint other = (ReflectPoint) obj; if (x != other.x) return false; if (y != other.y) return false; return true; } public String toString(){ return str1+";" + str2 + ";" + str3; } }
五、
反射的作用->实现框架功能
1.
什么是框架,例如,我们要写程序扫描.java文件中的注解,要解决哪些问题:读取每一样,在每一个中查找@,找到的@再去查询一个列表,如果@后的内容出现在了列表中,就说明这是一个我能处理和想处理的注解,否则,就说明它不是一个注解或者说至少不是一个我感兴趣和能处理的注解。接着就编写处理这个注解的相关代码。现在sun提供了一个apt框架,它会完成所有前期工作,只需要我们提供能够处理的注解列表,以及处理这些注解的代码。Apt框找到我们感兴趣的注解后通知或调用我们的处理代码去处理。
2.框架与框架要解决的核心问题
1)框架
我做房子卖给用户住,由用户自己安装门窗和空调,我做的房子就是框架,用户需要使用我的框架,把门窗插入进我提供的框架中。框架与工具类有区别,工具类被用户的类调用,而框架则是调用用户提供的类。
你做的门调用锁,锁是工具,你做的门被房子调用,房子是框架,房子和锁都是别人提供的。
2)框架要解决的核心问题
我在写框架(房子)时,你这个用户可能还在上小学,还不会写程序呢?我写的框架程序怎样能调用到你以后写的类(门窗)呢?
因为在写才程序时无法知道要被调用的类名,所以,在程序中无法直接new 某个类的实例对象了,而要用反射方式来做。
3.综合案例
先直接用new 语句创建ArrayList和HashSet的实例对象,演示用eclipse自动生成 ReflectPoint类的equals和hashcode方法,比较两个集合的运行结果差异。
然后改为采用配置文件加反射的方式创建ArrayList和HashSet的实例对象,比较观察运行结果差异。
引入了elipse对资源文件的管理方式的讲解。
4、简单框架程序的步骤:
1)右击项目名-->File-->命名,写入键值对:className=java.util.ArrayList,等号右边的可以自己定义集合的名称,即用户可以对此记事本修改成自己的类名。
2)代码实现,加载此文件:
①将文件读取到读取流中,一定要用完整的路径,可以使用getRealPath()方法获取路径名,再加上自己定义的文件夹名。
②用Properties类的load()方法将流加载经内存,即提取文件中的信息。
③关闭流:关闭的是读取流,因为流中的数据已经加载进内存。
3)通过getProperty()方法获取类名属性,将传入的类名赋值给指定变量。
4)用反射的方式,创建对象newInstance()
5)进行相关的具体操作。
5.类加载器
1)简述:类加载器是将.class的文件加载经内存,也可将普通文件中的信息加载进内存。
2)文件的加载问题:
①eclipse会将源程序中的所有.java文件加载成.class文件,以确保编译,然后放到classPath指定的目录中去。并且会将非.java文件原封不动的复制到.class指定的目录中去。在真正编译的时候,使用classPath目录中的文件,即放置.class文件的目录。
②写完程序是要讲配置文件放到.class文件目录中一同打包,这些都是类加载器加载的,资源文件(配置文件)也同样加载了配置文件。
③框架中的配置文件都要放到classPath指定的文件夹中,原因是它的内部就是用类加载器加载的文件。
3)资源文件的加载:是使用类加载器。
①由类加载器ClassLoader的一个对象加载经内存,即用getClassLoader()方法加载。若要加载普通文件,可用getResourseAsStream(String name)在classPath的文件中逐一查找要加载的文件。
②在.class身上也提供了方法来加载资源文件,其实它内部就是先调用了Loader方法,再加载的资源文件。
如:Reflect.class.getResourseAsStream(String name)
4)配置文件的路径问题:
第一、用绝对路径,通过getRealPath()方法运算出来具体的目录,而不是内部编码出来的。
一般先得到用户自定义的总目录,在加上自己内部的路径。可以通过getRealPath()方法获取文件路径。对配置文件修改是需要要储存到配置文件中,那么就要得到它的绝对路径才行,因此,配置文件要放到程序的内部。
第二、name的路径问题:
①如果配置文件和classPath目录没关系,就必须写上绝对路径,
②如果配置文件和classPath目录有关系,即在classPath目录中或在其子目录中(一般是资源文件夹resource),那么就得写相对路径,因为它自己了解自己属于哪个包,是相对于当前包而言的。
示例:
配置文件内容:
className=java.util.ArrayList
补充知识点:
程序中不处理异常,而是main方法声明抛出异常,便于大家可以集中看主要的关键代码。
Class类也提供getResourceAsStream方法的比喻:如果你每次都找我给你商店买可乐,那我还不如直接向你买可乐,即直接提供一个买可乐的方法给你。
程序示例:
import java.io.FileInputStream; import java.io.InputStream; import java.util.ArrayList; import java.util.Collection; import java.util.HashSet; import java.util.Properties; public class ReflectTest2 { public static void main(String [] args)throws Exception{ //读取系统文件到读取流中 //方式一: //InputStream ips = new FileInputStream("config.propert"); /*getRealPath()--得到完整的路径//如:金山词霸/内部 * 一定要用完整的路径,但完整的路径不是硬编码出来的,而是运算出来的。*/ //方式二: //InputStream ips = ReflectTest2.class.getClassLoader().getResourceAsStream("cn/itcast/text1/config.propert"); //方式三: //第一种:配置文件(资源文件)在当前包中 InputStream ips = ReflectTest2.class.getResourceAsStream("resourse/config.propert"); //第二种:配置文件(资源文件)不在当前包中,和此包没太大关系 //InputStream ips = ReflectTest2.class.getClassLoader().getResourceAsStream("cn/itcast/test2/resourse/config.properties"); //加载文件中的键值对 Properties props = new Properties(); props.load(ips); //关闭资源,即ips调用的那个系统资源 //注意:关闭的是ips操作的流,加载进内存后,就不再需要流资源了,需要关闭 ips.close(); //定义变量,将文件中的类名赋值给变量 String className = props.getProperty("className"); //通过变量,创建给定类的对象 Collection cons = (Collection)Class.forName(className).newInstance(); //将元素添加到集合中 /*Collection cons = new HashSet();*/ ReflectPoint pt1 = new ReflectPoint(3,3); ReflectPoint pt2 = new ReflectPoint(5,5); ReflectPoint pt3 = new ReflectPoint(3,3); cons.add(pt1); cons.add(pt2); cons.add(pt3); cons.add(pt1); //移除元素 cons.remove(pt1); System.out.println(cons.size()); } }
六’、内省与JavaBean
1.JavaBean(存在于java.bean包中)
1)JavaBean是一种特殊的Java类,主要用于传递数据信息,这种java类中的方法主要用于访问私有的字段,且方法名符合某种命名规则。
2)如果要在两个模块之间传递多个信息,可以将这些信息封装到一个JavaBean中,这种JavaBean的实例对象通常称之为值对象(Value Object,简称VO)。这些信息在类中用私有字段来存储,如果读取或设置这些字段的值,则需要通过一些相应的方法来访问,大家觉得这些方法的名称叫什么好呢?JavaBean的属性是根据其中的setter和getter方法来确定的,而不是根据其中的成员变量。如果方法名为setId,中文意思即为设置id,至于你把它存到哪个变量上,用管吗?如果方法名为getId,中文意思即为获取id,至于你从哪个变量上取,用管吗?去掉set前缀,剩余部分就是属性名,如果剩余部分的第二个字母是小写的,则把剩余部分的首字母改成小的。
setId()的属性名->id
isLast()的属性名->last
setCPU的属性名是什么?->CPU
getUPS的属性名是什么?->UPS
3)总之,一个类被当作javaBean使用时,JavaBean的属性是根据方法名推断出来的,它根本看不到java类内部的成员变量。
4)一个符合JavaBean特点的类可以当作普通类一样进行使用,但把它当JavaBean用肯定需要带来一些额外的好处,我们才会去了解和应用JavaBean!好处如下:
①在Java EE开发中,经常要使用到JavaBean。很多环境就要求按JavaBean方式进行操作,别人都这么用和要求这么做,那你就没什么挑选的余地!
②JDK中提供了对JavaBean进行操作的一些API,这套API就称为内省。如果要你自己去通过getX方法来访问私有的x,怎么做,有一定难度吧?用内省这套api操作JavaBean比用普通类的方式更方便。
2.内省
概述:
1、IntroSpector:即内省,是对内部进行检查,了解更多的底层细节。
2、内省的作用:主要针对JavaBean进行操作。
示例:
package cn.itcast.text1; import java.beans.BeanInfo; import java.beans.IntrospectionException; import java.beans.Introspector; import java.beans.PropertyDescriptor; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; public class IntroSpectorTest { /** * @param args */ /* * public static void main(String[] args) throws Exception { // TODO Auto-generated method stub ReflectPoint pt1 = new ReflectPoint(3,5); String propertyName = "x"; //"x"-->"X"-->"getX"-->MethodGetX--> //内省的方式: //属性描述符:PropertyDescriptor //get属性信息 PropertyDescriptor pd = new PropertyDescriptor(propertyName,pt1.getClass()); Method methodGetX = pd.getReadMethod(); Object retVal = methodGetX.invoke(pt1); System.out.println(retVal); //set属性信息 Object value = 7; PropertyDescriptor pd2 = new PropertyDescriptor(propertyName,pt1.getClass()); Method methodSetX = pd2.getWriteMethod(); methodSetX.invoke(pt1,value); System.out.println(pt1.getX()); } */ //上面的get或set代码分别通过选中要重构的代码,通过右击选重构获得get和set方法: public static void main(String[] args) throws Exception { // TODO Auto-generated method stub ReflectPoint pt1 = new ReflectPoint(3,5); String propertyName = "x"; //一般方式:"x"-->"X"-->"getX"-->MethodGetX--> //内省方式: //通过get和set方法获取属性值 Object retVal = getProperty(pt1, propertyName); System.out.println(retVal); Object value = 7; setProperty(pt1, propertyName, value); System.out.println(pt1.getX()); } //设置属性值的方法 //此处的类型为Object,通用,下同 private static void setProperty(Object rf, String propertyName, Object value) throws IntrospectionException, IllegalAccessException, InvocationTargetException { //创建属性描述符对象,将属性名称和加载文件等信息写入其中 PropertyDescriptor pd = new PropertyDescriptor(propertyName,rf.getClass()); //通过反射的方法类Method,获取属性所对应的set方法 Method methodSetX = pd.getWriteMethod(); methodSetX.invoke(rf, value); } //获取属性值的方法 private static Object getProperty(Object rf, String propertyName) throws IntrospectionException, IllegalAccessException, InvocationTargetException { //创建属性描述符对象,获取属性所对应的名称和加载文件等信息 PropertyDescriptor pd = new PropertyDescriptor(propertyName,rf.getClass()); //通过反射的方法类Method,获取属性所对应的get方法 Method methodGetX = pd.getReadMethod(); Object retVal = methodGetX.invoke(rf); return retVal; } }
3.
对JavaBean的复杂内省操作:
1)在IntroSpector类中有getBeanInfo(Class cls)的方法。
2)获取Class对象的Bean信息,返回的是BeanInfo类型。
3)BeanInfo类中有getPropertyDescriptors()的方法,可获取所有的BeanInfo的属性信息,返回一个PropertyDescriptor[]。
4)在通过遍历的形式,找出与自己想要的那个属性信息。
示例:(改写get方法)
… BeanInfo beanInfo = Introspector.getBeanInfo(pt1.getClass()); PropertyDescriptor[] pds = beanInfo.getPropertyDescriptors(); Object value = null; for(PropertyDescriptor pd : pds){ if(pd.getName().equals(propertyName)){ Method methodGetX = pd.getReadMethod(); value = methodGetX.invoke(pt1); break; } } …
这种方式要比上面的方法复杂些。
4.内省综合案例
1)演示用eclipse自动生成 ReflectPoint类的setter和getter方法。
2)直接new一个PropertyDescriptor对象的方式来让大家了解JavaBean API的价值,先用一段代码读取JavaBean的属性,然后再用一段代码设置JavaBean的属性。
3)演示用eclipse将读取属性和设置属性的流水帐代码分别抽取成方法:
只要调用这个方法,并给这个方法传递了一个对象、属性名和设置值,它就能完成属性修改的功能。
得到BeanInfo最好采用“obj.getClass()”方式,而不要采用“类名.class”方式,这样程序更通用。
4)采用遍历BeanInfo的所有属性方式来查找和设置某个RefectPoint对象的x属性。在程序中把一个类当作JavaBean来看,就是调用IntroSpector.getBeanInfo方法, 得到的BeanInfo对象封装了把这个类当作JavaBean看的结果信息。
5、BeanUtils工具包:
1.、BeanUtils等工具包都是由Apache开源组织提供的,为了便于开发。
2、BeanUtils可以将8种基本数据类型进行自动的转换,因此对于非基本数据类型,就需要注册转换器Converter,这就需要ConverUtils包
3.用eclipse如何加入jar包,先只是引入beanutils包,等程序运行出错后再引入logging包。
4.去掉JavaBean(ReflectPoint)的public修饰符时,BeanUtils工具包访问javabean属性时出现的问题。
2、好处:
1)提供的set或get方法中,传入的是字符串,返回的还是字符串,因为在浏览器中,用户输入到文本框的都是以字符串的形式发送至服务器上的,所以操作的都是字符串。也就是说这个工具包的内部有自动将整数转换为字符串的操作。
2)支持属性的级联操作,即支持属性链。如可以设置:人的脑袋上的眼镜的眼珠的颜色。这种级联属性的属性连如果自己用反射,那就很困难了,通过这个工具包就可以轻松调用。
3、可以和Map集合进行相互转换:可将属性信息通过键值对的形式作为Map集合存储(通过staticjava.util.Map describe(java.lang.Object bean)的方法),也可以将Map集合转换为JavaBean中的属性信息(通过static voidpopulate(java.lang.Object bean, java.util.Map properties)的方法)。
4、示例:
1)设置和获取属性值:
import java.lang.reflect.InvocationTargetException; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Date; import java.util.Map; import java.util.TreeMap; import org.apache.commons.beanutils.BeanUtils; import org.apache.commons.beanutils.ConversionException; import org.apache.commons.beanutils.ConvertUtils; import org.apache.commons.beanutils.Converter; import org.apache.commons.beanutils.PropertyUtils; import org.apache.commons.beanutils.locale.converters.DateLocaleConverter; import org.junit.Test; public class BeanUtilDemo { /** * BeanUtils使用 */ @Test public void test1() throws Exception{ //创建对象,设置属性值 Person p = new Person(); BeanUtils.setProperty(p, "name", "zzz"); String name = BeanUtils.getProperty(p, "name"); System.out.println(name); } @Test public void test2() throws Exception{ //创建对象,传入属性值 Person p = new Person(); String name = "wangwu"; String age = "23"; String hight = "173.5"; //设置属性值 BeanUtils.setProperty(p, "name", name); BeanUtils.setProperty(p, "age", age); BeanUtils.setProperty(p, "hight", hight); //获取属性值 System.out.println(BeanUtils.getProperty(p, "name")); System.out.println(BeanUtils.getProperty(p, "age")); System.out.println(BeanUtils.getProperty(p, "hight")); }2)未注册的属性值的获取和设置
//获取未注册的属性,即非八种基本数据类型的引用类型 //private Date birthday @Test public void test3() throws Exception{ Person p = new Person(); String name = "wangwu"; String age = "23"; String hight = "173.5"; String birthday = "1990-09-09"; ConvertUtils.register(new Converter() { //注册器Converter接口中方法的重写 @Override public Object convert(Class type, Object value) { if(value == null) return null; if(!(value instanceof String)) throw new ConversionException("只支持String类型的转换"); String str = (String) value; if(value.equals("")) return null; SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd"); try{ return sdf.parse(str); }catch(ParseException e){ throw new RuntimeException(e);//异常链不能掉,这里必须写上e } }}, Date.class); //测试 BeanUtils.setProperty(p, "name", name); BeanUtils.setProperty(p, "age", age); BeanUtils.setProperty(p, "hight", hight); BeanUtils.setProperty(p, "birthday", birthday); System.out.println(BeanUtils.getProperty(p, "name")); System.out.println(BeanUtils.getProperty(p, "age")); System.out.println(BeanUtils.getProperty(p, "hight")); System.out.println(BeanUtils.getProperty(p, "birthday")); } //使用已经写好的注册器DateLocaleConverter @Test public void test4() throws Exception{ Person p = new Person(); String name = "wangwu"; String age = "23"; String hight = "173.5"; String birthday = "1990-09-09"; //将日期注册到BeanUtils上 ConvertUtils.register(new DateLocaleConverter(), Date.class);//提供的注册器不健壮,因为传入空字符串,就会报错 //所以,当没有提供注册器或需要加强注册器的时候,可以自己写 //测试 BeanUtils.setProperty(p, "name", name); BeanUtils.setProperty(p, "age", age); BeanUtils.setProperty(p, "hight", hight); BeanUtils.setProperty(p, "birthday", birthday); System.out.println(BeanUtils.getProperty(p, "name")); System.out.println(BeanUtils.getProperty(p, "age")); System.out.println(BeanUtils.getProperty(p, "hight")); System.out.println(BeanUtils.getProperty(p, "birthday")); Date date = p.getBirthday(); System.out.println(date.toLocaleString()); }
3)Map集合在BeanUtils中的应用:
//Map集合在BeanUtils中的应用 @Test public void test5() throws Exception { /* * JDK 7.0新特性: * Map map = {"name" : "zs", "age" : 22, "hight" : 176.5}; */ //将数据存入集合 Map map = new TreeMap(); map.put("name", "zhangsan"); map.put("age", "20"); map.put("hight", "172.5"); map.put("birthday", "1999-10-02"); //注册器 ConvertUtils.register(new DateLocaleConverter(), Date.class); //获取属性 Person p = new Person(); BeanUtils.populate(p, map); System.out.println(BeanUtils.getProperty(p, "name")); System.out.println(BeanUtils.getProperty(p, "age")); System.out.println(BeanUtils.getProperty(p, "hight")); System.out.println(BeanUtils.getProperty(p, "birthday")); } //属性链 @Test public void test6() throws Exception { Person p = new Person(); BeanUtils.setProperty(p, "birthday.time", "111212"); System.out.println(BeanUtils.getProperty(p, "birthday.time")); }
4)
补充:
①BeanUtils是以字符串的形式进行操作的
②PropertyUtils是以传入值本身的类型进行操作的。
//PropertyUtils可直接解析为指定类型,而BeanUtils只能指定字符串的类型 @Test public void test7() throws Exception { Person p = new Person(); System.out.println("-----BeanUtiles-------"); BeanUtils.setProperty(p, "age", "22");//字符串形式 System.out.println(BeanUtils.getProperty(p, "age")); System.out.println(BeanUtils.getProperty(p, "age").getClass().getName()); System.out.println("-----PropertyUtiles-------"); PropertyUtils.setProperty(p, "age", 22);//Integer形式 System.out.println(PropertyUtils.getProperty(p, "age")); System.out.println(PropertyUtils.getProperty(p, "age").getClass().getName()); }注意:
①在前面内省例子的基础上,用BeanUtils类先get原来设置好的属性,再将其set为一个新值。
get属性时返回的结果为字符串,set属性时可以接受任意类型的对象,通常使用字符串。
②用PropertyUtils类先get原来设置好的属性,再将其set为一个新值。
get属性时返回的结果为该属性本来的类型,set属性时只接受该属性本来的类型。
最新最全的的java学习视频教程:http://pro.net.itcast.cn/View-22-1458.aspx -------android培训、java培训、java学习型技术博客、期待与您交流! ----------
详细请查看:http://edu.csdn.net/heima
- 黑马程序员 java高新技术<二>--java5的枚举、反射的深入讲解
- 黑马程序员--java高新技术--java5的枚举
- 黑马程序员--java高新技术--反射的深入讲解
- 黑马程序员--java高新技术--java5的注解
- 黑马程序员--java高新技术--动态代理技术的深入讲解
- 黑马程序员--java高新技术--java5的新特性
- 黑马程序员--java高新技术--java5的泛型
- 黑马程序员--java高新技术 25--枚举,反射
- Java高新技术-枚举和反射-----黑马程序员
- 黑马程序员 java高新技术<三>--java5的注解、java5的泛型
- 黑马程序员-java基础加强-反射的深入讲解
- 黑马程序员—反射的深入讲解
- 黑马程序员_Java高新技术_反射的深入探究
- 黑马程序员 (高新技术)反射技术的深入理解
- 黑马程序员_Java高新技术_反射的深入探究
- 黑马程序员--java高新技术--反射(二)
- 黑马程序员--java高新技术--类加载器的深入讲解与应用
- 黑马程序员 java高新技术<四>--类加载器、动态代理技术的深入讲解与应用
- android之Fragment(官网资料翻译)
- JavaScript 学习笔记(六)with(field) {……value.indexOf("@");……}、.focus()
- 在QT里添加图片资源
- Visual Studio 2010 更新NuGet Package Manager出错解决办法
- 循环实现递归
- 黑马程序员 java高新技术<二>--java5的枚举、反射的深入讲解
- poj2411 2663 2420 dp+状态压缩(多米诺骨牌问题)
- 10397 - Connect the Campus
- virtualbox 虚拟ubuntu如何全屏显示
- 实习第一周
- 调用c的printf
- 分割字符串 sscanf的用法
- BZOJ 2599 Race 点的分治
- HDOJ 1069