黑马程序员---高级-反射
2015-06-21 15:34
471 查看
——Java培训、Android培训、iOS培训、.Net培训、期待与您交流! ——-
一、类的加载、连接和初始化
在介绍反射之前,需要了解一下类的加载有关知识。
当程序主动使用某个类时,如果该类还未被加载到内存中,则系统就会通过加载、连接、初始化三个步骤来对该类进行初始化。
(1)过程
1.类加载指的是将类的class文件读入内存,并为之创建一个java.lang.Class对象,也就是说,当程序中使用任何类时,系统都会为之建立一个java.lang.Class对象。
2.类的连接,当类被加载之后,系统会为之生成一个对应的Class对象,接着将会进入连接阶段,连接阶段负责把类的二进制数据合并到JRE中。
3.类的初始化,虚拟机负责对类进行初始化,主要就是对类变量进行初始化。
(2)类初始化时机
当Java程序首次通过下面6中方式来使用某个类或接口是,系统就会初始化该类或接口。
1.创建该类的实例
2.调用某个类的类方法(静态方法)
3.访问某个类或接口的类变量,或为该类变量赋值
4.使用反射方式来强制创建某个类或接口对应的java.lang.Class对象
5.初始化某个类的子类
6.直接使用java.exe命令来运行某个主类
二、类加载器
类加载器负责将.class文件加载到内存中,并为之生成对应的java.lang.Class对象。
类加载器的组成
※1.Bootstrap ClassLoader:根类加载器
也被称为引导类加载器,它负责加载Java的核心类,比如System,String等。在JDK中JRE的lib目录下rt.jar文件中
※2.Extension ClassLoader:扩展类加载器
负责加载JRE的扩展目录,比如System,String等。在JDK中JRE的lib目录下rt.jar文件中
※3.System ClassLoader:系统类加载器
也被称为应用类加载器,负责在JVM启动时加载来自java命令的class文件,以及classpath环境变量所指定的jar包和类路径,程序可以通过ClassLoader的静态方法getSystemClassLoader()来获取系统类加载器。
三、反射
终于等到反射登场了。
反射就是通过class文件对象,去使用该文件中的成员变量,构造方法,成员方法。
(1)获取Class对象
前面已经介绍过了,每个类被加载之后,系统就会为该类生成一个对应的Class对象,通过该Class对象就可以访问到JVM中的这个类。在Java中获得Class对象通常有三种方式。
※方式一:调用对象的getClass()方法。它是Object类中的方法。
此方法是普通方法,不是静态的,要通过类的对象的引用去调用。
Student stu = new Studetn();
Class stuClass = stu.getClass();
※方式二:任何的”数据类型”都有一个”静态的属性”–class属性,这个属性可以
获取这个类的Class对象
※方式三(常用):调用Class类中的静态方法forName()可以获取某个类的Class对象;
public static Class forName(String className)
下面是具体的程序例子:
首先有个Student类
接着在测试类中运用上述三个方法来获取Class对象。
程序运行的结果是:
(2)获取构造方法
※批量获取
1.public Constructor[] getConstructors():获取所有公共构造方法
2.public Constructor[] getDeclaredConstructors():获取所有构造方法。包括公有,受保护,默认,私有
※获取单个的构造方法:
1.public Constructor getConstructor(Class… parameterTypes):获取类中的指定的”公有构造方法”。 参数:parameterTypes:构造方法形参的类型的Class
2.public Constructor getDeclaredConstructor(Class…parameterTypes):获取类中的任何的构造方法,包括私有的
调用构造方法:通过Constructor去调用。使用方法:public T newInstance(Object… initargs)
我们先来一个Student类,里面有被各种权限修饰符修饰的构造方法、成员变量和成员方法。
接着在测试类中来获取这些构造方法:
程序运行的结果是:
(3)获取成员变量
※ 批量获取:
1.Field[] getFields() :所用”公有”的成员属性
2.Field[] getDeclaredFields() :所有的成员属性,包括私有的:
※获取单个的属性
1.Field getField(String fieldName):获取单个的,公有的成员属性
2.Field getDeclaredField(String fieldName):获取单个的成员属性,可以是任何的访问级别
怎样设置和获取属性的值?
Field:
set(Object obj,Object value):设置值。注意:设置前,一定要先实例化一个此类的对象。
get(Object obj)
Student类还是用上一个类
测试类代码:
运行结果如下:
(4)获取成员方法
※批量获取:
1.Method[] getMethods() :获取所有的”公有”方法;
2.Method[] getDeclaredMethods() :获取所有方法,包括私有的。
※获取单个的:
1.
:获取某个公有的方法。 形参:name:方法名;parameterTypes:形参的类型列表;
2.
:获取某个方法。可以是私有的。
调用方法:
public Object invoke(Object obj,Object…args):调用此方法;注意:调用方法前,一定要先实例化一个此类对象。参数:obj : 方法所属的对象;args: 调用方法需要的实参
Student类依然用上面的那个。
接下来是测试类:
程序运行的结果是:
四、反射的应用
*(1)通过反射运行配置文件内容
首先有三个类Line1,Line2和Line3。后面的都是对前面的升级
接下来是测试类中的代码:
通过对反射的运用,可以极大的提高程序的维护性,不用担心需求改变后,或者功能升级后,大片的改动程序内容,运用反射后就只需要改变配置文件内容即可。
(2)通过反射越过泛型的检查
在使用结合的时候,jdk1,5以后加入了泛型,使得对添加到结合里的元素的类型做了规定,那么我们可以还能添加别的类型的元素吗?答案是可以的,使用反射就可以达到这个目的。
程序运行的结果是:
(3)写一个方法可以设置某个对象的某个属性为指定的值
首先有个Dog和Cat类:
接下来是测试类中的内容:
程序运行的结果是:
五、动态代理
在Java的java.lang.reflect包下提供了一个Proxy类和一个InvocationHandler接口,通过使用这个类和接口可以生成JDK动态代理类或动态代理对象。
如果在程序中为一个或多个接口动态地生成实现类,就可以使用Proxy来创建动态代理类;如果需要为一个或多个接口动态地创建实例,一个使用Proxy来创建动态代理类。
※Proxy提供的创建动态代理实例的方法是:
static Object newProxyInstance(ClassLoader loader,Class
[]interfaces,InvocationHandler h)
这个方法直接创建一个动态代理对象,该代理对象的实现类实现了interfaces指定的系类接口,执行代理对象的每个方法时都会被替换执行InvocationHandler对象的invoke方法。
※使用动态代理的步骤:
1).自定义一个类,实现InvocationHandler接口,重写内部的invoke()方法;
2).在需要代理类的时候,使用Proxy的newProxyInstance()方法获取代理类的对象
这里首先有个IDAO接口:
接着有TeacherDao和UserDao类实现了IDAO这个接口:
然后有个MyInvocationHandler类实现了InvocationHandler接口,重写了里面的invoke方法。
最后测试类中的代码:
程序运行的结果是:
一、类的加载、连接和初始化
在介绍反射之前,需要了解一下类的加载有关知识。
当程序主动使用某个类时,如果该类还未被加载到内存中,则系统就会通过加载、连接、初始化三个步骤来对该类进行初始化。
(1)过程
1.类加载指的是将类的class文件读入内存,并为之创建一个java.lang.Class对象,也就是说,当程序中使用任何类时,系统都会为之建立一个java.lang.Class对象。
2.类的连接,当类被加载之后,系统会为之生成一个对应的Class对象,接着将会进入连接阶段,连接阶段负责把类的二进制数据合并到JRE中。
3.类的初始化,虚拟机负责对类进行初始化,主要就是对类变量进行初始化。
(2)类初始化时机
当Java程序首次通过下面6中方式来使用某个类或接口是,系统就会初始化该类或接口。
1.创建该类的实例
2.调用某个类的类方法(静态方法)
3.访问某个类或接口的类变量,或为该类变量赋值
4.使用反射方式来强制创建某个类或接口对应的java.lang.Class对象
5.初始化某个类的子类
6.直接使用java.exe命令来运行某个主类
二、类加载器
类加载器负责将.class文件加载到内存中,并为之生成对应的java.lang.Class对象。
类加载器的组成
※1.Bootstrap ClassLoader:根类加载器
也被称为引导类加载器,它负责加载Java的核心类,比如System,String等。在JDK中JRE的lib目录下rt.jar文件中
※2.Extension ClassLoader:扩展类加载器
负责加载JRE的扩展目录,比如System,String等。在JDK中JRE的lib目录下rt.jar文件中
※3.System ClassLoader:系统类加载器
也被称为应用类加载器,负责在JVM启动时加载来自java命令的class文件,以及classpath环境变量所指定的jar包和类路径,程序可以通过ClassLoader的静态方法getSystemClassLoader()来获取系统类加载器。
三、反射
终于等到反射登场了。
反射就是通过class文件对象,去使用该文件中的成员变量,构造方法,成员方法。
(1)获取Class对象
前面已经介绍过了,每个类被加载之后,系统就会为该类生成一个对应的Class对象,通过该Class对象就可以访问到JVM中的这个类。在Java中获得Class对象通常有三种方式。
※方式一:调用对象的getClass()方法。它是Object类中的方法。
此方法是普通方法,不是静态的,要通过类的对象的引用去调用。
Student stu = new Studetn();
Class stuClass = stu.getClass();
※方式二:任何的”数据类型”都有一个”静态的属性”–class属性,这个属性可以
获取这个类的Class对象
※方式三(常用):调用Class类中的静态方法forName()可以获取某个类的Class对象;
public static Class forName(String className)
下面是具体的程序例子:
首先有个Student类
public class Student { }
接着在测试类中运用上述三个方法来获取Class对象。
public class Demo { public static void main(String[] args) throws ClassNotFoundException { Student stu = new Student(); //方式一: Class stuClass1 = stu.getClass(); //方式二: Class stuClass2 = Student.class; //方式三: // Class stuClass3 = Class.forName("Student");//运行时异常。这里需要的是:全名限定的类名(带包的类名) Class stuClass3 = Class.forName("cn.itcast.demo01_获取Class对象的三种方式.Student"); //对于Student的Class对象,三种方式获取的都是同一个Class对象。这个Class对象,在内存中只产生一个。 System.out.println("stuClass1 == stuClass2 :" + (stuClass1 == stuClass2)); System.out.println("stuClass1 == stuClass3 : " + (stuClass1 == stuClass3));; } }
程序运行的结果是:
(2)获取构造方法
※批量获取
1.public Constructor[] getConstructors():获取所有公共构造方法
2.public Constructor[] getDeclaredConstructors():获取所有构造方法。包括公有,受保护,默认,私有
※获取单个的构造方法:
1.public Constructor getConstructor(Class… parameterTypes):获取类中的指定的”公有构造方法”。 参数:parameterTypes:构造方法形参的类型的Class
2.public Constructor getDeclaredConstructor(Class…parameterTypes):获取类中的任何的构造方法,包括私有的
调用构造方法:通过Constructor去调用。使用方法:public T newInstance(Object… initargs)
我们先来一个Student类,里面有被各种权限修饰符修饰的构造方法、成员变量和成员方法。
public class Student { //********一些构造方法***********// //公有的 public Student(){ System.out.println("调用了公有的,无参的构造方法"); } public Student(boolean b){ System.out.println("调用了公有的,带参的构造方法。b = " + b); } //受保护的 protected Student(int n){ System.out.println("调用了受保护的构造方法 n = " + n); } //默认的 Student(String s){ System.out.println("调用了默认的构造方法。s = " + s); } //私有的 private Student(char c){ System.out.println("调用了私有的构造方法。c = " + c); } //*****************成员属性***************// public String stuName; protected int age; char sex; private String address; //*****************成员方法*******************// public void show(){ System.out.println("调用了公有,无参的show()方法"); } protected void show(String s){ System.out.println("调用了受保护的,带参的show()方法。参数s = " + s); } void show(int n){ System.out.println("调用了默认的,带参的show()方法。参数n = " + n); } private int show(char c){ System.out.println("调用了私有的,带参的带返回值的show()方法。参数c = " + c); return 10 + 20; } }
接着在测试类中来获取这些构造方法:
import java.lang.reflect.Constructor; public class ReflectGetConstrutorDemo { public static void main(String[] args) throws Exception { //1.获取Student的Class对象 Class stuClass = Class.forName("com.pizhihui.getconstructor.Student"); //2.获取构造方法: System.out.println("************获取所有\"公有\"构造方法*************"); //getConstructors():获取所有公共构造方法 Constructor[] conArray = stuClass.getConstructors(); for(Constructor c : conArray ){ System.out.println(c); } System.out.println("**********获取\"所有\"构造方法,包括公有、受保护、默认、私有************"); Constructor[] conArray2 = stuClass.getDeclaredConstructors(); for(Constructor c : conArray2){ System.out.println(c); } System.out.println("***********获取单个,公有的,无参的构造方法******* ***"); Constructor con1 = stuClass.getConstructor(); System.out.println(con1); System.out.println("*******获取单个,公有的,带参的构造方法***************"); Constructor con2 = stuClass.getConstructor(boolean.class); System.out.println(con2); // Constructor con3 = stuClass.getConstructor(int.class);//java.lang.NoSuchMethodException // System.out.println(con3); System.out.println("*********获取单个,受保护的构造方法**************"); Constructor con3 = stuClass.getDeclaredConstructor(int.class); System.out.println(con3); System.out.println("***********获取单个,私有的构造方法*************"); Constructor con4 = stuClass.getDeclaredConstructor(char.class); System.out.println(con4); System.out.println("**********调用公有,无参的构造方法*********"); Object obj = con1.newInstance(); System.out.println("obj = " + obj); System.out.println("*******调用公有,带参的构造方法************"); Student stu = (Student)con2.newInstance(true); System.out.println("**********调用受保护的构造方法***********"); obj = con3.newInstance(20); System.out.println("*******调用私有的构造方法***************"); //由于要访问是私有成员,所以这里要设置"取消访问限制检查",进行暴力访问 con4.setAccessible(true); obj = con4.newInstance('男'); } }
程序运行的结果是:
(3)获取成员变量
※ 批量获取:
1.Field[] getFields() :所用”公有”的成员属性
2.Field[] getDeclaredFields() :所有的成员属性,包括私有的:
※获取单个的属性
1.Field getField(String fieldName):获取单个的,公有的成员属性
2.Field getDeclaredField(String fieldName):获取单个的成员属性,可以是任何的访问级别
怎样设置和获取属性的值?
Field:
set(Object obj,Object value):设置值。注意:设置前,一定要先实例化一个此类的对象。
get(Object obj)
Student类还是用上一个类
测试类代码:
import java.lang.reflect.Method; public class ReflectGetVariableDemo { public static void main(String[] args) throws Exception{ //1.获取Student的Class对象 Class stuClass = Class.forName("com.pizhihui.getvariable.Student"); //2.批量获取成员方法 System.out.println("**************获取所有\"公有\"的成员方法************"); Method[] methodArray = stuClass.getMethods();//包含了父类的方法 for(Method m : methodArray){ System.out.println(m); } System.out.println("**********获取所有的成员方法,包括私有的**************"); Method[] methodArray2 = stuClass.getDeclaredMethods();//不包含父类的。 for(Method m : methodArray2){ System.out.println(m); } System.out.println("******获取单个\"公有\"的\"无参\"的成员方法*********"); Method m1 = stuClass.getMethod("show"); System.out.println(m1); System.out.println("*********获取单个\"私有\"的\"带参\"的成员方法*********"); Method m2 = stuClass.getDeclaredMethod("show", char.class); System.out.println(m2); System.out.println("**********调用\"公有\"的\"无参\"的成员方法*********"); //先获取对象 Object stuObj = stuClass.getConstructor().newInstance(); m1.invoke(stuObj); System.out.println("********调用\"私有\"的\"带参\"的\"带返回值\"的成员方法*********"); //暴力访问 m2.setAccessible(true); Object result = m2.invoke(stuObj, '男'); System.out.println("返回值为:" + result); } }
运行结果如下:
(4)获取成员方法
※批量获取:
1.Method[] getMethods() :获取所有的”公有”方法;
2.Method[] getDeclaredMethods() :获取所有方法,包括私有的。
※获取单个的:
1.
:获取某个公有的方法。 形参:name:方法名;parameterTypes:形参的类型列表;
2.
:获取某个方法。可以是私有的。
调用方法:
public Object invoke(Object obj,Object…args):调用此方法;注意:调用方法前,一定要先实例化一个此类对象。参数:obj : 方法所属的对象;args: 调用方法需要的实参
Student类依然用上面的那个。
接下来是测试类:
import java.lang.reflect.Method; public class ReflectGetMethodDemo { public static void main(String[] args) throws Exception{ //1.获取Student的Class对象 Class stuClass = Class.forName("com.pizhihui.getmethod.Student"); //2.批量获取成员方法 System.out.println("********获取所有\"公有\"的成员方法*********"); Method[] methodArray = stuClass.getMethods();//包含了父类的方法 for(Method m : methodArray){ System.out.println(m); } System.out.println("********获取所有的成员方法,包括私有的*********"); Method[] methodArray2 = stuClass.getDeclaredMethods();//不包含父类的。 for(Method m : methodArray2){ System.out.println(m); } System.out.println("********获取单个\"公有\"的\"无参\"的成员方法********"); Method m1 = stuClass.getMethod("show"); System.out.println(m1); System.out.println("*******获取单个\"私有\"的\"带参\"的成员方法********"); Method m2 = stuClass.getDeclaredMethod("show", char.class); System.out.println(m2); System.out.println("********调用\"公有\"的\"无参\"的成员方法************"); //先获取对象 Object stuObj = stuClass.getConstructor().newInstance(); m1.invoke(stuObj); System.out.println("****调用\"私有\"的\"带参\"的\"带返回值\"的成员方法********"); //暴力访问 m2.setAccessible(true); Object result = m2.invoke(stuObj, '男'); System.out.println("返回值为:" + result); } }
程序运行的结果是:
四、反射的应用
*(1)通过反射运行配置文件内容
首先有三个类Line1,Line2和Line3。后面的都是对前面的升级
public class Line1 { public void show(){ System.out.println("线路1"); } } public class Line2 { public void fun(){ System.out.println("线路2"); } } public class Line3 { public void method(){ System.out.println("线路3"); } }
接下来是测试类中的代码:
import java.io.FileReader; import java.io.IOException; import java.lang.reflect.Method; import java.util.Properties; public class Demo { public static void main(String[] args) throws Exception { // Line1 line = new Line1(); // line.show(); //升级 // Line2 line = new Line2(); // line.fun(); //通过反射,反向加载LineXxxx类 // Class aClass = Class.forName("cn.itcast.demo05_练习_通过反射运行配置文件内容.Line2");//更改点1 Class aClass = Class.forName(getValue("className")); //实例化对象 Object obj = aClass.getConstructor().newInstance(); //获取fun方法 Method m = aClass.getMethod(getValue("methodName"));//更改点二 //调用fun方法 m.invoke(obj); } //一个方法,通过某个键,获取配置文件中的值 public static String getValue(String key) throws IOException{ Properties pro = new Properties(); FileReader fileIn = new FileReader("line.properties"); pro.load(fileIn); fileIn.close(); return pro.getProperty(key); } }
通过对反射的运用,可以极大的提高程序的维护性,不用担心需求改变后,或者功能升级后,大片的改动程序内容,运用反射后就只需要改变配置文件内容即可。
(2)通过反射越过泛型的检查
在使用结合的时候,jdk1,5以后加入了泛型,使得对添加到结合里的元素的类型做了规定,那么我们可以还能添加别的类型的元素吗?答案是可以的,使用反射就可以达到这个目的。
import java.lang.reflect.Method; import java.util.ArrayList; /* * 定义一个具有Integer类型泛型的集合对象,问:怎么可以向这个集合中添加一个字符串; */ public class Demo { public static void main(String[] args) throws Exception{ ArrayList<Integer> intList = new ArrayList<>(); intList.add(10); // intList.add("abc"); //通过反射,直接去加载ArrayList的Class,然后调用它的add方法 Class listClass = ArrayList.class; //获取add方法 Method addMethod = listClass.getMethod("add", Object.class); //调用add方法 addMethod.invoke(intList, "abc"); //遍历集合 System.out.println("遍历集合:"); for(Object o : intList){ System.out.println(o); } } }
程序运行的结果是:
(3)写一个方法可以设置某个对象的某个属性为指定的值
首先有个Dog和Cat类:
public class Dog { private int age; public int getAge(){ return this.age; } } public class Cat { private String name; public String getName(){ return this.name; } }
接下来是测试类中的内容:
import java.lang.reflect.Field; /* * 写一个通用的设置某个对象的某个属性为指定的值的方法 */ public class Demo { public static void main(String[] args) throws Exception { //将Cat对象的name属性设置为"波斯猫" Cat c = new Cat(); setValue(c,"name","波斯猫"); System.out.println("验证:取值:" + c.getName()); //为Dog对象的age属性赋值 Dog d = new Dog(); setValue(d,"age",2); System.out.println("验证:" + d.getAge()); } //定义一个方法,这个方法能够为某个对象的某个属性,设定为某个值; public static void setValue(Object obj , String fieldName,Object value) throws Exception{ //使用反射 //1.获取obj的Class对象 Class aClass = obj.getClass(); //2.获取fileName属性。可能是私有的 Field f = aClass.getDeclaredField(fieldName); //3.有可能是私有的,在访问之前,暴力一下 f.setAccessible(true); //4.赋值 f.set(obj, value); } }
程序运行的结果是:
五、动态代理
在Java的java.lang.reflect包下提供了一个Proxy类和一个InvocationHandler接口,通过使用这个类和接口可以生成JDK动态代理类或动态代理对象。
如果在程序中为一个或多个接口动态地生成实现类,就可以使用Proxy来创建动态代理类;如果需要为一个或多个接口动态地创建实例,一个使用Proxy来创建动态代理类。
※Proxy提供的创建动态代理实例的方法是:
static Object newProxyInstance(ClassLoader loader,Class
[]interfaces,InvocationHandler h)
这个方法直接创建一个动态代理对象,该代理对象的实现类实现了interfaces指定的系类接口,执行代理对象的每个方法时都会被替换执行InvocationHandler对象的invoke方法。
※使用动态代理的步骤:
1).自定义一个类,实现InvocationHandler接口,重写内部的invoke()方法;
2).在需要代理类的时候,使用Proxy的newProxyInstance()方法获取代理类的对象
这里首先有个IDAO接口:
public interface IDAO { public void add(); public void update(); public void delete(); public void find(); }
接着有TeacherDao和UserDao类实现了IDAO这个接口:
public class UserDao implements IDAO{ public void add(){ System.out.println("添加一条User信息"); } public void update(){ System.out.println("修改一条User信息"); } public void delete(){ System.out.println("删除一条User信息"); } public void find(){ System.out.println("查询一条User信息"); } } public class TeacherDao implements IDAO { @Override public void add() { System.out.println("添加一条Teacher信息"); } @Override public void update() { System.out.println("修改一条Teacher信息"); } @Override public void delete() { System.out.println("删除一条Teacher信息"); } @Override public void find() { System.out.println("查询一条Teacher信息"); } }
然后有个MyInvocationHandler类实现了InvocationHandler接口,重写了里面的invoke方法。
import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; public class MyInvocationHandler implements InvocationHandler { private Object target;//需要代理的对象; public MyInvocationHandler(Object target){ this.target = target; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { // check(); //调用原方法 Object result = method.invoke(target, args);//这里调用的就是我们在Demo的main()方法中想执行的方法 log(); return result; } private void check(){ System.out.println("权限检查......"); } private void log(){ System.out.println("写入日志......"); } }
最后测试类中的代码:
public class Demo { public static void main(String[] args) { IDAO userDao = (IDAO)Proxy.newProxyInstance(UserDao.class.getClassLoader(), UserDao.class.getInterfaces(), new MyInvocationHandler(new UserDao())); userDao.add(); userDao.update(); userDao.delete(); userDao.find(); //为TeacherDao生成一个代理对象 IDAO teaDao = (IDAO)Proxy.newProxyInstance(TeacherDao.class.getClassLoader(), TeacherDao.class.getInterfaces(), new MyInvocationHandler(new TeacherDao())); teaDao.add(); teaDao.update(); teaDao.delete(); teaDao.find(); } }
程序运行的结果是:
相关文章推荐
- 黑马程序员_Java_多线程
- 《一个程序员的生命周期》读后感
- 黑马程序员---API
- 面试题23 从上往下打印二叉树
- 【剑指offer 面试题15】链表中倒数第K个结点
- 程序员的量化交易之路(38)--Lean之实时事件处理接口IRealTimeHandler和RealTimeEvent6
- 程序员《人,绩效和职业道德》博客读后感
- 前端面试高频题:删除数组重复元素的多种方法
- 剑指offer--面试题23:从上往下打印二叉树--Java实现
- 《人,绩效和职业道德》读后感想
- 黑马程序员--我的基础测试题及答案
- 人and绩效and职业道德
- 《C程序员:从校园到职场》出版预告(2):从“百花齐放”到“一枝独秀”
- 读后感之《一个程序猿的生命周期》和《构建之法——人、绩效和职业道德》
- 读《人,绩效和职业道德》有感
- 据说年薪30万的Android程序员必须知道的帖子【转载】
- 《人,绩效和职业道德》----有感
- 《人,绩效和职业道德》及《一个程序猿的生命周期》读后感
- 剑指offer 面试题2
- 《人,绩效和职业道德》及博客读后感