Java基础加强
Java基础加强
Junit的使用
JUnit是一个Java语言的单元测试框架。Junit是xUnit(xUnit是一套基于测试驱动开发的测试框架)家族中的一个子集,在c++,paython,java语言中测试框架的名字都不相同。
- junit4是一个单元测试框架,既然是框架,这也就意味着jdk并没有为我们提供api,因此在这里我们就需要导入相关的依赖。
其中的断言机制:将程序预期的结果与程序运行的最终结果进行比对,确保对结果的可预知性。
JUnit的作用:
JUnit用来为程序写测试用例。
以前总是需要自己写个main方法来测试某个方法。当需要测试另一个方法时,还要在main中再写一段代码对另一个方法进行测试。
创建测试类:
- 首先需要去下载JUnit的jar包以及一个依赖包hamcrest.core,并将其导入到项目当中。
- 因为我用的是IDEA,所以需要进行相关配置,这里就不进行过多赘述。
- 创建一个测试目录
- 选择create new test,创建测试类,如果想在已有测试类中添加方法,选择同目录,同类名,更新类中方法,和创建新类相同
或者可以在想要被测试的类中使用Alt+inset(或者导航栏中点击Code-Generator)快捷键,选择JUnit-JUnit4,就会自动生成当前类的测试类:
插件默认会测试所有方法,使用快捷键Ctrl+Shift+T可以选择性的测试部分方法,非常的方便:
使用注解方式运行测试方法:
在测试类的方法的上面输入
@Test:表示方法进行单元测试
@Test :标识这个方法 需要进行测试.
@Ignore :忽略 这个方法不参与测试.
@Before :标记在每个测试方法之前都会执行
@After :标记在每个测试方法之后都会执行
@BeforeClass:标记 会在测试方法之前执行一次 这个方法必须是static
@AfterClass:标记 会在测试方法之后执行一次 这个方法必须是static
public class TestDemo2Test {
@Before
public void before() throws Exception {
System.out.println("before");
}
@After
public void after() throws Exception {
System.out.println("after");
}
@Test
public void testTest02() throws Exception {
System.out.println("test02");
}
@Test
public void testTest01() throws Exception{
System.out.println("test01");
}
@Ignore
public void testTest03() throws Exception{
System.out.println("test03");
}
}
结果:
以上就是我们的单元测试,需要遵循以下规则:
1、每一个测试方法上使用@Test进行修饰
2、每一个测试方法必须使用public void 进行修饰
3、每一个测试方法不能携带参数
4、测试代码和源代码在两个不同的项目路径下
5、测试类的包应该和被测试类保持一致
6、测试单元中的每个方法必须可以独立测试
以上的6条规则,是在使用单元测试的必须项,当然junit也建议我们在每一个测试方法名加上test前缀,表明这是一个测试方法。
assertEquals是一个断言的规则,里面有两个参数,第一个参数表明我们预期的值,第二个参数表示实际运行的值。
import junit.framework.Assert;
import org.junit.Test;
public class TestDemo2 {
@Test
public void test02() {
int a = 3;
int b = 5;
int sum = a+b;
//使用断言
//Assert.assertEquals("测试期望的值", "方法运行的实际的值")
Assert.assertEquals(80, sum);
}
}
结果:
JDK5.0新特性
JDK5中新增了很多新的java特性,利用这些新语法可以帮助开发人员编写出更加高效、清晰,安全的代码。
- 泛型
- 枚举
- 静态导入
- 自动装箱/拆箱
- for/in语句(增强for循环)
- 可变参数
- 注解、字符串格式化、Java5线程并发库
泛型
概述
-
数组与集合
Java中可以定义任意类型的属性,例如String[]中存放的就是String类型的数据,我们称之为持有String类型的数组。但1.5之前时,Java的集合类却只能持有Object类型,1.5时添加了泛型的概念,泛型允许Java创建持有任意类型的集合对象,例如:new ArrayList<String>()表示这个ArrayList中只能持有String类型的对象。 -
类型变量(参数)
具有一个或多个类型参数的类就是泛型类。
泛型类都至少有一个类型变量,你需要在创建泛型类对象时给类型变量赋值。当然,你要给类型变量赋的值必须是一个类型!
ArrayList arr = new ArrayList();
其中String就是给ArrayList类的类型变量赋值。在ArrayList类中所有使用类型变量的地方都会被String所替换。例如:boolean add(E e),其中e的类型就是变量,它会被String替换,最终变成boolean add(String e)。E get(int index)方法中返回值的类型为变量,它也会被String替换,最终变成String get(int index)。 -
泛型的好处
将运行期遇到的问题转移到了编译期。例如ArrayList类的add()方法参数还是Object类型,当然get()方法的返回值类型也是Object。这就说明使用get()方法获返回值后,你还需要强转。错误的强转可能会出现ClassCastException。
ArrayList list = new ArrayList();
list.add(“hello”);
Integer i = (Integer)list.get(0);//运行时会出错,但编码时发现不了这个问题在有了泛型之后就不会再有了。
ArrayList list = new ArrayList();
list.add(“hello”);//编译出错!
Integer i = (Integer)list.get(0);
很明显,是泛型把只能在运行时才能找到的错误推向了编译期,这就是泛型的优点。
注意:泛型是提供给javac编译器使用的,它用于限定集合的输入类型,让编译器在源代码级别上,即挡住向集合中插入非法数据。但编译器编译完带有泛形的java程序后,生成的class文件中将不再带有泛形信息,以此使程序运行效率不受到影响,这个过程称之为“擦除”。
泛型使用在集合上
package cn.itcast.test03;
import java.util.*;
public class Demo {
public static void main(String[] args) {
Map<String,String> map=new HashMap<String,String>();
map.put("01","a");
map.put("02","b");
map.put("03","c");
//Map集合的遍历方式
//第一种,通过key和value的映射关系,先获得所有key的set集合,再通过get方法传递key值获得value
Set<String> set=map.keySet();
for(String i:set){
System.out.println(i+":"+map.get(i));
}
System.out.println("===========");
//第二种,通过定义在Map集合内部的key和value的关系数据类型Map.Entry<K,V>,调用它的方法获得key和value值
Set<Map.Entry<String,String>> entryMap=map.entrySet();
for(Map.Entry<String,String> i:entryMap){
i.getKey();
i.getValue();
}
//-------------------
//Set集合的遍历方式
Set<String> set1=new HashSet<String >();
set1.add("aaaa");
set1.add("bbbb");
set1.add("cccc");
System.out.println("===========");
//迭代器
Iterator<String> iterator=set1.iterator();
while (iterator.hasNext()){
System.out.println(iterator.next());
}
//增强for
for(String i:set1){
System.out.println(i);
}
System.out.println("===========");
//------------------
//List集合的遍历方式
//遍历list集合 有几种方式 三种
//普通for循环 迭代器 增强for
//普通for循环
List<String> list = new ArrayList<String>();
list.add("aaa");
list.add("bbb");
list.add("ccc");
//使用普通for
for(int i=0;i<list.size();i++) {
String s = list.get(i);
System.out.println(s);
}
System.out.println("=================");
//使用增强for
for (String s1 : list) {
System.out.println(s1);
}
System.out.println("=================");
//使用迭代器遍历
Iterator<String> it = list.iterator();
while(it.hasNext()) {
System.out.println(it.next());
}
}
}
泛型方法
泛型方法定义:
泛型类是说明这个类有类型变量,在创建这个类对象时需要给类型变量赋值。
泛型方法是说明这个方法有类型变量,在调用这个方法时需要给类型变量赋值。
Java程序中的普通方法、构造方法和静态方法中都可以使用泛型。 方法使用泛形前,必须对泛形进行声明,语法:<T> ,T可以是任意字母,但通常必须要大写。通常需放在方法的返回值声明之前。例如:
public <T> T get(T[] ts, int index) {
return ts[index];
}
注意,在返回值前面定义类型变量。
get()方法是一个泛型方法,它有一个类型变量T,这说明在调用get()方法时需要给get()方法的T赋值。
如果要定义一个有意义的泛型方法,那么:
-
参数需要使用类型变量;
-
返回值需要使用类型变量。
所以,通常在调用泛型方法时,只需要传递参数就可以了,例如:String[] strs = "abc"; String s = o.get(strs, 0);
上面代码中给get()方法的类型变量T赋值为String,因为传递的参数为String数组,所以就是给T赋值为String。当然,也可以显示给出类型变量的值:o.<String>get(strs,0);
,在点后面,方法名前面给出类型值,但一般人不会这么打代码。
注意:
- 只有对象类型才能作为泛型方法的实际参数(例如:Integer)。
- 在泛型中可以同时有多个类型,例如:
public static <K,V> V getValue(K key) { return map.get(key);}
泛型类
-
如果一个类多处都要用到同一个泛型,这时可以把泛形定义在类上(即类级别的泛型),语法格式如下:
public class GenericDao<T> { private T field1; public void save(T obj){} public T getId(int id){} }
-
注意,静态方法不能使用类定义的泛形,而应单独定义泛形
1 自定义泛型类的语法
自定义泛型类的语法基本与定义正常的法一样:
public class A<T> {}
定义泛型类时,需要在类名后面给出一对尖括号,在尖括号中给出1~N个类型变量。
2 泛型类中使用类型变量
用户在使用泛型类时,需要为类型变量赋值。例如:new A()。
在泛型类中可以使用类型变量:
public class A<T> {
private T t;
public A(T t) {
this.t = t;
}
public T get() {
return t;
}
}
当用户创建A类对象时,就会给A类的类型变量T赋值,例如:new A(),这说明在A类中所有的T都会被String替换。
public class A {
private String t;
public A(String t) {
this.t = t;
}
public String get() {
return t;
}
}
枚举
概述
什么是枚举类型
word文档的对齐方式有几种:左对齐、居中对齐、右对齐;
开车的方向有几种:前、后、左、右;
枚举就是有限实现个数的类型,你可能会说,byte类型也只有256个,没错,但我们真实定义为枚举的类型,一般最多也就十多个实例,再多就不会定义为枚举了。
JDK1.4之前的枚举类型
在JDK1.4之前没有枚举类型,都是使用int或字符串类型来表示枚举,如果枚举只有两个选项,那么连int都用不上,只需要使用boolean类型即可。
例如:BorderLayout类的方位给出五个:CENTER、EAST、SOUTH、WEST、NORTH。当使用容器类的方法添加组件时:add(new Button(), “CENTER”),这是合法的,但因为String类型太过宽泛,所以可能会出现add(new Button(), “哈哈”)的调用可能,这会导致运行时出现异常。所以,JDK1.5又新增了枚举类型。
定义枚举类型
定义枚举类型需要使用enum关键字,例如:
public enum Direction {
FRONT、BEHIND、LEFT、RIGHT;
}
Direction d = Direction.FRONT;
注意,每个枚举选项之间是用逗号隔开的。如果枚举类没有构造器、方法等,在最后一个枚举选择后面可以不打分号。但是如果枚举类还有其他成员,那么就要在最后一个枚举项后面添加分号了。
Direction类型只有四个选项,你可以理解为这个枚举类只有四个实例对象一样。外界无法去创建新的枚举对象,只能从这四个中去选择。
其实大多数时候,我们使用枚举类型还是与以及使用int或String表示的枚举一样,基本上都是很简单的。
枚举与switch
1.5开始枚举类型可以在switch中使用!在1.7之后,String类型也可以放到switch中使用了。
Direction d = Direction.FRONT;
switch(d) {
case FRONT: System.out.println("前面");break;
case BEHIND:System.out.println("后面");break;
case LEFT: System.out.println("左面");break;
case RIGHT: System.out.println("右面");break;
default:System.out.println("错误的方向");
}
Direction d1 = d;
System.out.println(d1);
注意,在switch中,不能使用枚举类名称,例如:“case Direction.FRONT:”这是错误的,因为编译器会根据switch中d的类型来判定每个枚举类型,在case中必须直接给出与d相同类型的枚举选项,而不能再有类型。
枚举类特性
枚举类具有如下特性:
- 枚举类也是一种特殊形式的Java类。
- 枚举类中声明的每一个枚举值代表枚举类的一个实例对象。
- 与java中的普通类一样,在声明枚举类时,也可以声明属性、方法和构造函数,但枚举类的构造函数必须为私有的。
枚举被设计成是单例模式,即枚举类型会由JVM在加载的时候,实例化枚举对象,你在枚举类中定义了多少个就会实例化多少个,JVM为了保证每一个枚举类元素的唯一实例,是不会允许外部进行new的,所以会把构造函数设计成private,防止用户生成实例,破坏唯一性。 - 枚举类也可以实现接口、或继承抽象类。
- JDK5中扩展了swith语句,它除了可以接收int, byte, char, short外,还可以接收一个枚举类型。
- 若枚举类只有一个枚举值,则可以当作单态设计模式使用。
除了不能继承,基本上可以将 enum 看做一个常规的类。
常用方法
Java中声明的枚举类,均是java.lang.Enum类的子类,它继承了Enum类的所有方法。
常用方法:
- int compareTo(E e):比较两个枚举常量谁大谁小,其实比较的就是枚举常量在枚举类中声明的顺序,例如FRONT的下标为0,BEHIND下标为1,那么FRONT小于BEHIND;
- boolean equals(Object o):比较两个枚举常量是否相等;
- int hashCode():返回枚举常量的hashCode;
- String name():返回枚举常量的名字;
- int ordinal():返回枚举常量在枚举类中声明的序号,第一个枚举常量序号为0;
- String toString():把枚举常量转换成字符串;
- static T valueOf(Class enumType, String name):把字符串转换成枚举常量。
注意:枚举类也可以有构造器,但构造器不能给出访问修饰,而且默认都是private构造器。因为枚举类的实例不能让外界来创建!
枚举类也是类,也可以有构造器、方法和属性,只是对构造器有一些限制而已。在语法上有一些怪异罢了!
如果枚举不添加任何方法,枚举值默认为从0开始的有序数值。### 枚举类的属性 枚举类也可以有属性。但是,如果每个枚举常量的属性值如果都相同,那就失去了意义,我们需要让每个枚举常量的属性值不同,那么就需要自己使用构造器来创建枚举常量,然后在构造器中给每个枚举常量传递不同的值。
enum Direction {
//因为本类没有无参构造器,所以每个常量都需要调用本类的有参构造器。你可能会说,为什么没有new,这是枚举的独有语法,你需要习惯它。
FRONT("qianmian"), BEHIND("houmian"), LEFT("zuomian"), RIGHT("youmian");
//给本类添加一个实例属性explain
private String explain;
//为本类设置一个有参构造器,本类只有这一个构造器,那么说明在声明枚举常量时必须调用本构造器。每个常量都会传递一个字符串给自己的explain属性。
Direction(String explain) {
this.explain = explain;
}
public void setExplain(String explain) {
this.explain = explain;
}
public String getExplain() {
return explain;
}
}
public class Demo
{
public static void main(String[] args){
//调用枚举常量的方法!
System.out.println(Direction.FRONT.getExplain());
}
}
静态导入
什么是静态导入
静态导入也需要使用import关键字;
静态导入后,在调用静态方法,以及使用静态属性时就可以不再给出类名了,例如向控制台打印时可以把System.out.println()写成out.println();System.exit(0)写成exit(0)。
静态导入的语法格式
- import static 包名.类名.静态方法名;
- import static 包名.类名.静态属性名;
- import static 包名.类名.*;
不建议使用!
使用静态导入,使代码可读性降低!
自动拆装箱
什么是自动装箱拆箱
在1.5之后,Java允许把基本类型与其对应的包装器类型之间自动相互转换。
例如:
Integer i = 100;
//把int类型的100直接给了Integer类型的变量i,这就是自动装箱。
int a = new Integer(100);
//这是自动拆箱。
Object o = 100;
//其实是把100自动装箱为Integer,即Object o = Integer.valueOf(100);
int a = (Integer)o;
//其实是把o强转为Integer后,自动拆箱为int,即int a=((Integer)o).intValue();
。
基本数据类型 | 对象包装类 |
---|---|
byte | Byte |
short | short |
int | Integer |
long | Long |
boolean | Boolean |
float | Float |
double | Double |
char | Character |
注意:String不属于基本数据类型
Integer.valueOf()与Integer内部缓存
我们已经知道自动装箱使用的是Integer.valueOf()方法,但我们要了解一下,其实valueOf()方法会使用Integer类内部的缓存来获取Integer对象。
Integer类的内部缓存了-128~127之间的256个Integer对象,如果valueOf()方法需要把这个范围之内的整数转换成Integer对象时,valueOf()方法不会去new对象,而是从缓存中直接获取,这就会导致valueOf(100)两次,都是从缓存中获取的同一个Integer对象!
Integer i1 = Integer.valueOf(100);
Integer i2 = Integer.valueOf(100);
boolean b = i1 == i2;//结果为true
//相同的道理:
Integer i1 = 100;
Integer i2 = 100;
boolean b = i1 == i2;//结果为true
//但是:
Integer i1 = 200;
Integer i2 = 200;
boolean b = i1 == i2;//结果为false
这是因为200不在Integer内部的缓存之内,所以这时valueOf()方法会new一个Integer对象。每次valueOf(200)都会创建一个新的Integer对象,所以才会是false。
public class TestDemo2 {
public static void main(String[] args) {
doSomething(10);
}
public static void doSomething(double m) {
System.out.println("double......");
}
public static void doSomething(Integer a){
System.out.println("integer.....");
}
}
执行的结果是会调用 doSomething(double m)
- 首先在jdk1.4里面肯定调用这个方法,如果调用下面的方法,需要类型转换,但是jdk1.4不能实现自动拆装箱
- 由于jdk是向下兼容,所以在jdk1.4中调用了这个方法,在jdk5.0里面还是会调用这个方法
增强for
增强for循环概念
可以循环遍历数组或者集合类。
增强for循环的语法格式
for(元素类型 e : 数组或集合对象) {
}
增强for每循环一次,都会把数组或集合中的一个元素赋值给e,从头开始遍历,直到最后一个元素。
增强for的优缺点
- 只能从头到尾的遍历数组或集合,而不能只遍历部分。
- 在遍历List或数组时,不能获取当前元素下标。
- 增强for使用便简单,这是它唯一的优点了。
- 增强for比使用迭代器方便一点,引入增强for循环的原因就是为了替代Iterator
增强for与Iterable接口
任何实现了Iterable接口的类,都可以使用增强for来遍历。
可变参数
使用数组为方法参数:
int sum(int a, int b) {return a + b;}
int sum(int a, int b, int c) {return a + b;}
int sum(int a, int b, int c, int d) {return a + b + c + d;}
看上面代码。我们知道这种重载是无止境的!
当函数的参数可以是0~n个时,我们最好的办法就是使用数组来处理,例如把上面代码修改为一个函数:
int sum(int[] arr) {
int sum = 0;
for(int i = 0; i < arr.length; i++) {
sum +=arr[i];
}
return sum;
}
修改后的sum()方法可以计算0~N个整数的和,但调用sum()需要传递一个数组,这使调用这个函数很不方便。
int arr = {1,2,3,,4,5,5};
sum(arr);
可变参数方法的定义:
可以把数组类型的参数定义为可变参数,例如:
int sum(int… arr) {
int sum = 0;
for(int i = 0; i < arr.length; i++) {
sum +=arr[i];
}
return sum;
}
上面代码把int[] arr修改为int… arr,其中arr就变成了可变参数。可变参数其实就是数组。
调用可变参数方法:
当调用int sum(int…arr)方法时就方便多了。如下方式的调用都是正确的:
- int[] arr = {1,2,3}; sum(arr);,使用数组调用;
- sum();,不给参数调用,表示传递0个元素的数组,即:int[] arr={}; sum(arr);
- sum(5);,用一个参数调用,表示传递1个元素的数组,即:int[] arr={5}; sum(arr);
- sum(2,3,4,5);,用多个参数调用,表示传递多个元素的数组,即:int[] arr={2,3,4,5}; sum(arr);。
调用可变参数方法,可以传递0~N个参数来调用,也可以直接传递数组来调用。
注意事项:
- 调用可变参数的方法时, 编译器将自动创建一个数组保存传递给方法的可变参数,因此,程序员可以在方法体中以数组的形式访问可变参数;
- 可变参数只能处于参数列表的最后, 所以一个方法最多只能有一个长度可变的参数;
- 可变参数只能出现在方法的形参中,局部变量或属性是不能使用这种东西的。
反射
反射的原理
什么是反射?
让我们从Class类开始了解反射!
每个加载到方法区中的class文件都对应一个Class类的对象,你可以把Class类的对象理解为硬盘上的class文件的对应体。
- 首先需要把java文件保存到本地硬盘 .java
- 编译java文件,生成.class文件
- 使用jvm,把class文件通过类加载加载到内存中
- 因为万物皆对象,class文件在内存中使用Class类表示
- 当使用反射时候,首先需要获取到Class类,得到了这个类之后,就可以得到class文件里面的所有内容(包含属性 构造方法 普通方法)
类 Class
构造方法 Constructor
成员变量 Field
方法 Method
反射的作用
反射是Java中的高级特性,在各种Java框架中都需要使用反射。所以,就算你将来很长一段时间不使用反射,但你使用的框架都大量使用了反射,所以想深入学习框架,那么就一定要学习反射。
框架通常通过反射来识别一个对象的“类型信息”。当你传递给框架一个对象时,框架会通过反射来了解对象的真实类型(对象实体的类型,而不是引用的类型),这个类型有几个构造器,有什么样的属性,有什么样的方法。还可以通过反射调用构造器,调用方法,对属性进行读写操作。
你可能觉得这没有什么神奇的,那是你还没了解我说的是什么!你需要再想一想,写一个方法,参数是Object obj,然后你的方法需要创建一个与参数类型相同的对象出来,还要调用这个对象上的方法。需要注意,参数是Object类型,但用户调用这个方法时,可能传递的不是Object实体对象,它的真实类型有可能是任何类型。
public static void fun(Object obj) {
- 通过反射创建obj相同类型的对象;
- 调用obj的方法,调用的方法可以是obj真实类型独有的方法。而不一定非要Object中的方法。
}
Class类
-
Java中java.lang.Class类用于表示一个类的字节码(.class)文件
-
如何得到某个class文件对应的Class对象
- 已知类和对象的情况下:
类名.class
对象.getClass() ---- Object类中的方法 - 未知类和对象的情况下
Class.forName(“包名.类名”)
- 已知类和对象的情况下:
-
Class类代表某个类的字节码,并提供了加载字节码的方法:forName(“包名.类名”)
forName方法用于加载类字节码到内存中,并封装成一个Class对象- forName方法用于加载类字节码到内存中,并封装成一个Class对象
Constructor类
-
Constructor类的实例对象代表类的一个构造方法
-
得到某个类所有的构造方法
Constructor [] constructors= Class.forName(“java.lang.String”).getConstructors(); -
得到指定的构造方法并调用
Constructor constructor = Class.forName(“java.lang.String”).getConstructor(String.class);
String str = (String)constructor.newInstance(“abc”);
从JDK1.9版本开始,Class.newInstance()已经被弃用了。
新的创建方法是调用Class类的getDeclaredConstructor()方法。//返回当前 Class 对象表示的类的指定已说明的一个构造方法:getDeclaredConstructor().newInstance()
-
Class类的newInstance()方法用来调用类的默认构造方法
String obj =(String)Class.forName(“java.lang.String”).newInstance();
Field类
- Field类代表某个类中的一个成员变量,并提供动态的访问权限
- Field对象的获得
- 得到所有的成员变量
Field[] fields = c.getFields(); // 取得所有public属性(包括父类继承)
Field[] fields = c.getDeclaredFields(); // 取得所有声明的属性 - 得到指定的成员变量
Field name = c. getField(“name”);
Field name = c. getDeclaredField(“name”);
- 得到所有的成员变量
- 设置Filed变量是否可以访问
field. setAccessible(boolean); - Field变量值的读取、设置
field.get(obj)
filed.set(obj,value);
Method类
-
Method类代表某个类中的一个成员方法
-
Method对象的获得
- 获得所有方法
getDeclaredMethods()
getMethods() - 获得指定的方法
getDeclaredMethod(String name, Class<?>… parameterTypes)
getMethod(String name, Class<?>… parameterTypes)
- 获得所有方法
-
通过反射执行方法
invoke(Object obj, Object… args)
如果执行static方法 ,第一个参数obj 传入 null
使用反射操作构造方法
Person类:
import java.lang.reflect.Constructor;
public class Demo {
public static void main(String[] args) throws Exception{
Class c1 = Class.forName("cn.itcast.test09.Person");
//获取无参构造函数
//Constructor constructor1=c1.getConstructor(); //已经被弃用
Object constructor1=c1.getDeclaredConstructor().newInstance();
Person p1=(Person)constructor1;
p1.setName("zhangsan");
System.out.println(p1.getName());
//获取有参构造函数
Constructor constructor2=c1.getDeclaredConstructor(String.class,String.class);
Person p2=(Person)constructor2.newInstance("lisi","01");
System.out.println(p2.getId()+":"+p2.getName());
}
}
要点:
- Class类中的newInstance()方法已经被弃用(虽然还可以使用),从JDK1.9版本开始获取构造方法的实例采用的是:
getDeclaredConstructor().newInstance()
,即使用构造方法类中的newInstance()方法。
getDeclaredConstructor()可以传入类类型的可变参数,应该是根据传入的参数类型去调用相应的构造方法,而以前只能调用无参构造器
使用反射操作属性
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
public class Demo {
public static void main(String[] args) throws Exception{
Class c1 = Class.forName("cn.itcast.test09.Person");
Person p1=(Person) c1.getDeclaredConstructor().newInstance();//获得Person类实例
Field f1=p1.getClass().getDeclaredField("name");//获得name属性
f1.setAccessible(true);//设置可以操作私有属性
f1.set(p1,"zhangsan");//相当于 在 p1.name = "zhangsan";
System.out.println(f1.get(p1)); //返回该所表示的字段的值 Field ,指定的对象上。
}
}
要点:
- 当我们要访问的字段被private修饰时,isAccessible()得到的值是false,必须要改成true才可以访问,即:
Field.setAccessible(true);
使用反射操作基本方法
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
public class Demo {
public static void main(String[] args) throws Exception{
Class c1 = Class.forName("cn.itcast.test09.Person");
Person p1=(Person) c1.getDeclaredConstructor().newInstance();//获得Person类实例
//c4.getDeclaredMethods();//得到所有的普通方法
Method m1=c1.getDeclaredMethod("setName", String.class);//获得方法实例
//操作的私有的方法 ,需要设置值是true
//m1.setAccessible(true);
m1.invoke(p1,"zhangsan");//让setName方法执行 ,执行设置值
System.out.println(p1.getName());
}
}
要点:
- 执行方法:
Object invoke(Object obj, Object… args)
传入实例对象和对应参数的值 - 如果要执行静态方法,第一个参数obj 传入 null