JavaWeb学习笔记(Java基础加强)

Java基础加强

Junit的使用

JUnit是一个Java语言的单元测试框架。Junit是xUnit(xUnit是一套基于测试驱动开发的测试框架)家族中的一个子集,在c++,paython,java语言中测试框架的名字都不相同。

  • junit4是一个单元测试框架,既然是框架,这也就意味着jdk并没有为我们提供api,因此在这里我们就需要导入相关的依赖。

其中的断言机制:将程序预期的结果与程序运行的最终结果进行比对,确保对结果的可预知性。

JUnit的作用:
JUnit用来为程序写测试用例。
以前总是需要自己写个main方法来测试某个方法。当需要测试另一个方法时,还要在main中再写一段代码对另一个方法进行测试。

创建测试类:

  1. 首先需要去下载JUnit的jar包以及一个依赖包hamcrest.core,并将其导入到项目当中。
  2. 因为我用的是IDEA,所以需要进行相关配置,这里就不进行过多赘述。
  3. 创建一个测试目录
    在这里插入图片描述
  4. 选择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)。

静态导入的语法格式

  1. import static 包名.类名.静态方法名;
  2. import static 包名.类名.静态属性名;
  3. 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();

基本数据类型对象包装类
byteByte
shortshort
intInteger
longLong
booleanBoolean
floatFloat
doubleDouble
charCharacter

注意: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个参数来调用,也可以直接传递数组来调用。

注意事项:

  1. 调用可变参数的方法时, 编译器将自动创建一个数组保存传递给方法的可变参数,因此,程序员可以在方法体中以数组的形式访问可变参数;
  2. 可变参数只能处于参数列表的最后, 所以一个方法最多只能有一个长度可变的参数;
  3. 可变参数只能出现在方法的形参中,局部变量或属性是不能使用这种东西的。

反射

反射的原理

什么是反射?
让我们从Class类开始了解反射!
在这里插入图片描述

每个加载到方法区中的class文件都对应一个Class类的对象,你可以把Class类的对象理解为硬盘上的class文件的对应体。
在这里插入图片描述

  1. 首先需要把java文件保存到本地硬盘 .java
  2. 编译java文件,生成.class文件
  3. 使用jvm,把class文件通过类加载加载到内存中
  4. 因为万物皆对象,class文件在内存中使用Class类表示
  5. 当使用反射时候,首先需要获取到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());
    }
}

要点:

  1. 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 ,指定的对象上。
    }
}

要点:

  1. 当我们要访问的字段被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());
    }
}

要点:

  1. 执行方法:Object invoke(Object obj, Object… args)传入实例对象和对应参数的值
  2. 如果要执行静态方法,第一个参数obj 传入 null
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值