14--反射技术

1、反射技术

1.1、介绍反射

Java中的反射:它是使用非常规的手段获取某个class文件中的内容(成员变量、构造函数、普通函数等)。不使用new关键字创建对象,而是通分析过class文件中的内容,获取到类的对象,进而可以让类中的函数运行。

所有的框架(Struts2、hibernate、spring、springMVC、mybatis、springboot等)它们的底层全部在使用反射技术。

反射技术:关注的是class文件。

1.2、介绍class文件

任何的类、接口它们通过javac命令编译之后都会生成class文件。反射就是解剖class文件中的东西(成员变量、构造函数、普通函数等),并让其运行的。

要想在程序使用反射技术:第一步必须是先获取Class的对象。获取到Class对象,等价于获取到了某个真正存在的class文件(字节码文件)。

在Java中任何的class文件就是Class的一个实例。我们使用反射技术,其实就是想解析某个class文件中到底有什么,最后是希望将这个class文件里面的东西运行起来。

Class类没有公开的构造函数,所以我们在程序是不能创建Class的对象,但程序中我们还要使用Class对象(使用class文件)。

只要有Class对象存在,肯定有一个class文件和这个Class对象是一一对应关系。

只要JVM将某个class文件从硬盘上加载内存中的时候,JVM就会根据当前这class文件创建出Class的一个对象(当我们通过java命令,让JVM先运行起来,JVM就会加载当前需要使用的所有的class文件)。只要JVM加载一个class文件,堆中就会多一个Class的对象。class文件和堆中的Class对象是唯一的(一一对应)。

获取到Class对象,其实就是获取到class文件。

1.3、获取Class的对象

Class的对象(class文件)获取有三种方式:

  1. getClass方法
  2. .class属性
  3. forName方法

1.3.1、getClass方法获取

getClass方法它是通过某个类已经存在的对象,然后获取到这个类的class文件夹。

例如:Person.class。目标是要获取Person.class文件,但是现在已经有Person对象了。通过Person对象,就能够获取Person.class文件。

getClass方法被定义在了Object类中。

	/*
	 * 演示使用Object类中的getClass方法获取某个类的class文件
	 */
	public static void demo() {
		// 直接通过new关键字创建Person类的对象
		Person p = new Person();
		/*
		 *  使用getClass方法 : 获取到了class文件,
		 *  就是获取到Class的对象
		 */
		Class clazz = p.getClass();
		System.out.println(clazz);
		
		// 获取字符串的class文件
		Class<? extends String> clazz2 = "".getClass();
		System.out.println(clazz2);
		
		/*
		 * 在Java中任何的类型,都有一个class文件,任何的类型都可以是Class的对象
		 * 任何类型:
		 * 	  8种基本类型:byte、short、int、long、float、double、char、boolean
		 * 	     数组类型:
		 *    类类型:
		 *    接口类型:
		 */
		// 获取数组对应的Class对象
		int[] arr = new int[4];
		Class<? extends int[]> clazz3 = arr.getClass();
		System.out.println(clazz3);
	}

1.3.2、class属性

任何类型(基本类型和引用类型)都有一个class属性,可以直接获取其对应的class文件(Class对象)。

		/*
	 * Java为了保证任何一个类型(基本类型和复杂、引用类型)都可以获取Class的对象
	 * 因此强制给任何一个类型中都添加了一个class的属性名(静态的成员变量)。并且是静态的。
	 */
	public static void demo2() {
		
		// 获取自定义类的class文件
		Class clazz = Person.class;
		System.out.println(clazz);
		
		// 获取String的class文件
		Class clazz2 = String.class;
		System.out.println(clazz2);
		
		// 获取接口的class文件
		Class clazz3 = List.class;
		System.out.println(clazz3);
		
		// 获取数组(真正是不存在)
		Class clazz4 = int[].class;
		System.out.println(clazz4);
		
		// 基本类型的class文件(真正是不存在)
		Class clazz5 = double.class;
		System.out.println(clazz5);
	}

1.3.3、使用Class类中的静态forName

	/*
	 *  在Class类中有个静态的forName方法,
	 *  可以通过一个字符串来获取字符串所表示的那个class文件
	 */
	public static void demo3() throws ClassNotFoundException {
		
		/*
		 * 获取String的class文件
		 * 
		 * 如果使用Class类中的forName方法获取Class的对象,
		 * 传递的字符串必须是  包名.类名  的格式。
		 */
		Class clazz = Class.forName("java.lang.String");
		System.out.println(clazz);
		
		// 获取Person类的class文件
		Class clazz2 = Class.forName("com.kuaixueit.classs.Person");
		System.out.println(clazz2);
		
		// 获取Map接口的class文件
		Class clazz3 = Class.forName("java.util.Map");
		System.out.println(clazz3);
		
	}

1.3.4、创建类的对象

在常规的情况下,创建类的对象会使用new关键字,通过构造方法创建对象。假设现在我们需要得到Person类的对象。但是我们不通过new关键字创建。

使用反射技术创建:

  1. 先获取到Class的对象。Person.class
  2. 调用Class类中的newInstance方法,就可以得到Person类的一个对象

/*
 * 通过Class类中的newInstance方法获取某个类的具体的真实对象
 */
public class GetInstance {
	public static void main(String[] args) throws Exception {
		// 在以前需要某个类的对象的时候通过new关键字创建
		Person p = new Person("赵四");
		System.out.println(p);
		
		// 通过反射的方式获取Person的对象
		
		// 获取Class对象
		Class clazz = Class.forName("com.kuaixueit.classs.Person");
		
		/*
		 *  调用newInstance方法
		 *  如果想使用Class类中的newInstance方法创建某个类的对象,
		 *  要求这个类中必须有公开的无参数的构造方法。
		 *  newInstance 的内部就是在分析当前Class对象所表示的class文件中的
		 *  那个空参数的方法。分析出来之后,再通过构造方法创建出这个类的对象
		 */
		Object obj = clazz.newInstance();
		System.out.println(obj);
	}
}

1.4、演示反射类中的成员

1.4.1、反射(解析)class文件中的构造方法

使用Class类中的方法:

/*
 * 演示反射类中的构造方法
 */
public class ReflectConstructor {
	
	public static void main(String[] args) throws Exception {
		demo3();
	}
	public static void demo3()  throws Exception{
		// 反射的第一步,必须是获取Class对象
		Class clazz = Class.forName("com.kuaixueit.classs.Person");
		/*
		 * getConstructors 获取到的是类中所有public格式的构造方法
		 */
		Constructor[] cons = clazz.getConstructors();
		for (Constructor c : cons) {
			System.out.println(c);
		}
		System.out.println("========================");
		/*
		 * getDeclaredConstructors 获取到类中所有的构造方法(包含公开和私有的)
		 */
		Constructor[] cons2 = clazz.getDeclaredConstructors();
		for (Constructor c : cons2) {
			System.out.println(c);
		}
	}
	// 反射类中私有的构造方法
	public static void demo2() throws Exception {
		// 反射的第一步,必须是获取Class对象
		Class clazz = Class.forName("com.kuaixueit.classs.Person");
		/*
		 * 只要是反射类中的私有的成员(变量、构造方法、一般方法):
		 *   一般都会使用Class类中的getDeclaredXxxxxx
		 *   
		 * getDeclaredConstructor(Class<?>... parameterTypes)
		 * Class<?>... parameterTypes : 被反射的构造方法的参数列表的类型对应的class文件
		 */
		Constructor cons = clazz.getDeclaredConstructor( String.class , int.class );
		/*
		 * 当反射到类中的私有的成员的时候,一般都需要强制取消Java对权限的检查操作
		 * setAccessible(boolean flag)  ,取消权限的检查
		 * 一定要在使用被反射的私有内容之前取消检查
		 */
		cons.setAccessible(true);
		Object object = cons.newInstance("华安",23);
		System.out.println(object);
		
	}
	// 反射类中非私有,有参数的构造方法
	public static void demo() throws Exception {
		// 反射的第一步,必须是获取Class对象
		Class clazz = Class.forName("com.kuaixueit.classs.Person");
		/*
		 * 在一个类中可以有多个构造方法,它们以重载的方式存在。
		 * 多个构造方法之间的区分:
		 * 	参数列表的类型。
		 * getConstructor(Class<?>... parameterTypes) :
		 * 获取到类中指定的某个公开(public修饰)的构造方法
		 * Class<?>... parameterTypes : 它就是要反射的那个构造方法中的参数的类型
		 * 只是这里要写的是类型对应的class文件,而不是直接写这个类型
		 */
		/*
		 * 返回的Constructor对象,
		 * 表示的就是当前正好从类中获取的那个class文件中的
		 * 某个构造方法
		 */
		Constructor cons = clazz.getConstructor( String.class );
		/*
		 * 使用Constructor类中的newInstance方法来创建出被反射的这个类的对象
		 * newInstance(Object... initargs) 
		 * Object... initargs : 构造方法真正运行的时候需要接受的参数
		 */
		Object obj = cons.newInstance("秋香");
		System.out.println( obj );
	}
}

1.4.2、反射成员变量

类中成员变量分为:静态成员和和非非静态的成员变量。

成员变量的使用:非静态的必须和创建这个类的对象的时候一起出现。只有静态的通过类名可以直接使用。

非静态成员:我们即使反射到类中的成员变量,依然需要这个类的对象。也就是必须明确这个成员变量具体属于哪个对象。

/*
 * 反射类中的成员变量
 */
public class ReflectField {
	public static void main(String[] args) throws Exception {
		demo4();
	}
	// 获取所有的成员变量
	public static void demo4() throws Exception {
		// 获取Class对象
		Class clazz = Class.forName("com.kuaixueit.classs.Person");
		/*
		 * getFields : 获取到的当前类以及从父类继承或是从接口中得到的所有public的成员变量
		 */
		Field[] fields = clazz.getFields();
		for (Field f : fields) {
			System.out.println(f);
		}
		System.out.println("--------------------------");
		/*
		 * getDeclaredFields : 只是获取本类中的所有成员变量(私有和公开都有)
		 */
		Field[] fields2 = clazz.getDeclaredFields();
		for (Field f : fields2) {
			System.out.println(f);
		}
		
	}
	// 反射类中的静态的成员变量
	public static void demo3() throws Exception {
		// 获取Class对象
		Class clazz = Class.forName("com.kuaixueit.classs.Person");
		
		// 反射私有静态的成员变量
		Field field = clazz.getDeclaredField("addr");
		// 私有需要取消权限
		field.setAccessible(true);
		
		/*
		 *  由于是静态的不需要对象
		 *  set(Object obj, Object value) 
		 *  在设置对象的地方直接写null,就表示不需要对象
		 */
		field.set(null, "西安");
		// 创建对象
		Object obj = clazz.newInstance();
		System.out.println(obj);
	}
	// 反射类中私有的非静态的成员变量
	public static void demo2() throws Exception {
		// 获取Class对象
		Class clazz = Class.forName("com.kuaixueit.classs.Person");
		
		// 反射私有
		Field field = clazz.getDeclaredField("name");
		// 私有的在使用之前,需要取消权限检查
		field.setAccessible(true);
		// 由于成员变量是非静态,需要对象
		Object obj = clazz.newInstance();
		System.out.println(obj);
		
		// 给成员变量设置数据
		field.set(obj, "华夫人");
		System.out.println(obj);
	}
	// 反射类中公开非私有的成员变量
	public static void demo() throws Exception {
		// 获取Class对象
		Class clazz = Class.forName("com.kuaixueit.classs.Person");
		/*
		 * 反射成员:
		 * getField(String name) 
		 * String name : 需要被反射的那个成员变量的名称
		 */
		Field field = clazz.getField("age");
		/*
		 * 反射到类中的非静态的成员变量,这时必须还要得到这个类的对象
		 * 也就是必须要明确当前反射的这个非静态的成员变量到底属于那个对象
		 * 中的成员变量
		 */
		Object obj = clazz.newInstance();
		System.out.println(obj);
		/*
		 * 反射了成员变量之后,可以使用Field类中的set方法给成员变量赋值
		 * set(Object obj, Object value) 
		 * Object obj : 成员变量所依赖的类的对象
		 * Object value : 当前这个成员变量所要设置的具体的数据
		 */
		field.set(obj, 44);
		
		// 打印
		System.out.println(obj);
	}
}

1.4.3、反射类中的成员方法

在一个类中的方法如何区分:

         需要通过方法名和参数列表的类型进行区分。

构造方法:通过参数列表区分。

Method类中的:

它的功能是将被反射的方法运行起来。

/*
 * 演示反射类中的方法:	
 */
public class ReflectMethod {
	public static void main(String[] args) throws Exception {
		demo4();
	}
	// 获取所有的方法
	public static void demo4() throws Exception {
		// 获取Class对象
		Class clazz = Class.forName("com.kuaixueit.classs.Person");
		/*
		 * getMethods : 当前类、以及父类或接口中的方法
		 */
		Method[] methods = clazz.getMethods();
		for (Method m : methods) {
			System.out.println(m);
		}
		
		System.out.println("=====================");
		/*
		 * getDeclaredMethods : 获取到当前类中的方法(私有和非私有的)
		 */
		Method[] methods2 = clazz.getDeclaredMethods();
		for (Method m : methods2) {
			System.out.println(m);
		}
	}
	/*
	 * 反射类中私有,静态,没有参数,没有返回值的方法
	 */
	public static void demo3() throws Exception {
		// 获取Class对象
		Class clazz = Class.forName("com.kuaixueit.classs.Person");
		/*
		 * 反射的方法如果不需要参数,在获取方法的时候,
		 * 参数列表的对应的class文件的地方书写null
		 */
		Method method = clazz.getDeclaredMethod("func", null);
		// 私有的,需要取消权限
		method.setAccessible(true);
		/*
		 * 由于方法是静态的就需要对象,同时方法也不参数参数,因此在调用invoke的时候,直接在参数的地方写null
		 */
		method.invoke(null, null);
	}
	/*
	 * 反射类中的非静态,私有的,需要参数,有返回值的方法
	 */
	public static void demo2() throws Exception {
		
		// 获取Class对象
		Class clazz = Class.forName("com.kuaixueit.classs.Person");
		
		// 私有的方法
		Method method = clazz.getDeclaredMethod("show", int.class);
		
		// 非静态方法,需要对象
		Object obj = clazz.newInstance();
		// 由于是私有的,因此需要取消权限检查
		method.setAccessible(true);
		
		// 让方法运行
		Object  value = method.invoke(obj, 123);		
		System.out.println(value);
	}
	/*
	 * 演示类中的非静态的,公开的,需要参数,但没有返回值的方法
	 */
	public static void demo() throws Exception {
		
		// 获取Class对象
		Class clazz = Class.forName("com.kuaixueit.classs.Person");
		
		/*
		 *  反射类中方法
		 *  getMethod(String name, Class<?>... parameterTypes) 
		 *  String name :被反射的方法名
		 *  Class<?>... parameterTypes : 被反射的方法的参数列表的类型对应的classs文件
		 */
		Method method = clazz.getMethod("setName", String.class);
		/*
		 * 由于方法的非静态,类中的非静态的方法运行需要当前类的对象来调用。
		 */
		// 反射一个对象
		Object obj = clazz.newInstance();
		System.out.println(obj);
		/*
		 * 反射到类中的方法,最终的目标是让方法能够运行
		 * Object invoke(Object obj, Object... args) 
		 * 
		 * 返回值Object:被反射的方法运行的时候返回的结果。
		 * 参数:
		 * 	Object obj :被反射的方法运行的时候需要的对象
		 *  Object... args : 被反射的方法运行的时候需要接受的实际的参数
		 */
		method.invoke(obj, "张三"); // 被反射的方法就会运行
		System.out.println(obj);
	}
}

2、内部类

类本身就是描述事物。有时可能会出现事物中还嵌有其他的事物。

例如:人,身体内部会有一个心脏。

2.1、内部类的代码体现

2.1.1、成员内部类

在描述类的时候,将一个类写在另外一个类的成员位置。书写成员位置上的这个类,就和我们学习的类中的其他的成员(变量、方法)使用没有太大的区别。

// 普通的类,但是这个类的成员位置上有其他的类
class Outer{
	
	private int x = 123;
	public Outer(){
	}
	
	public void show(){
		System.out.println("show run .....");
	}
	
	/*
	 * 在成员位置上定义了一个类,就可以使用成员修饰符来修饰
	 * 这个位于成员位置上的类
	 */
	public class Inner{
		private int y = 456;
		public void func(){
			System.out.println("y = " + y);
		}
	}
}

研究如何使用成员内部类中的成员:
public class InnerClassDemo {
	public static void main(String[] args) {
		// 创建出外部类的对象
		Outer out = new Outer();
		// 通过对象调用方法
		out.show();
		
		// 成员上public的非静态的内部类
		//Outer.Inner in = out.new Inner();
		Outer.Inner in = new Outer().new Inner();
		in.func();
	}
}

在外部类中添加如下代码:

	/*
	 * 静态成员内部类
	 */
	public static class Inner2{
		private int y = 456;
		public void func(){
			System.out.println("y = " + y);
		}
	}
		/*
		 * 如果成员内部类是public的静态格式,就不需要外部类的对象
		 */
		Outer.Inner2 in2 = new Outer.Inner2();
		in2.func();

类的成员位置上如果有私有的成员内部类,这时这个成员内部类在外部类以外的地方也是无法被访问的。但是可以在外部类的其他方法中访问。

2.1.2、局部内部类

将类定义在其他类的方法的内部。

/*
 * 演示使用局部内部类
 */
class Outer2{
	
	public void show(){
		System.out.println("show run....");
		// 局部内部类,不能
	}
	
	public void method(){
		System.out.println("method run.....");
		/*
		 * 在方法中定义一个类,这个类属于局部内部类,
		 * 这个类只能在这个方法中使用,在这个方法以外的地方无法访问到
		 */
		class Inner{
			private int x = 123;
			public void func(){
				System.out.println("x = " + x);
			}
		}
		
		/*
		 * 创建类的对象,调用局部内部类中的内容
		 */
		Inner in = new Inner();
		in.func();
	}
	
}

public class InnerClassDemo2 {
	public static void main(String[] args) {
		Outer2 out = new Outer2();
		out.method();
	}
}
class Outer3{
	private int x = 456;
	public void method(){
		int y = 147;
		/*
		 * 在方法中定义一个类,这个类属于局部内部类,
		 * 这个类只能在这个方法中使用,在这个方法以外的地方无法访问到
		 */
		class Inner{
			private int x = 123;
			public void func(){
				int x = 789;
				System.out.println("局部内部类中的局部变量 x = " + x);
				System.out.println("局部内部类中的成员变量 x = " + Inner.this.x);
				System.out.println("外部类中中的成员变量 x = " + Outer3.this.x);
				/*
				 * 外部类方法中的局部变量,在局部内部类中使用的时候,这个外部类的局部变量是被final修饰的
				 * 在JDK8之后,可以不写final关键字。但是正在final关键字依然存在的。
				 * y = 123;
				 */
			}
		}
		System.out.println("y = " + y);
		
		/*
		 * 创建类的对象,调用局部内部类中的内容
		 */
		Inner in = new Inner();
		in.func();
	}
}

public class InnerClassDemo3 {
	public static void main(String[] args) {
		Outer3 out = new Outer3();
		out.method();
	}
}

2.2、匿名内部类

匿名内部类的书写格式:

     new 父类(抽象类、一般类)或接口(  ) {

         复写父类或接口的方法;

};

上面的这个格式,是在创建对象的同时复写类或接口中的方法。而没有定义引用变量指向这个对象,所有这个对象已经属于匿名对象。

使用匿名内部类大部分的情况下都会使用下面的代码书写:        

父类或接口类型  引用变量名  =  new 父类或接口(  ){

         复写父类或接口中的方法;

};

/*
 * 演示匿名内部类
 */
// 接口
interface Inter{
	public void show();
}
class Outer4{
	public void func(){
		/*
		 * 在这里想使用接口中的show方法,
		 * 需要定义一个类,实现接口,并实现其中的show方法
		 */
		class InterImpl implements Inter{

			public void show() {
				System.out.println("演示....");
			}
		}
		// 创建实现类的对象
		InterImpl in = new InterImpl();
		// 调用实现类中复写的方法
		in.show();
	}
	public void method(){
		// 演示使用匿名内部类代替上面func方法中的InterImpl类
		new Inter(){
			public void show() {
				System.out.println("匿名内部类复写的show");
			}
		}.show();
		
		Inter in = new Inter(){
			public void show() {
				System.out.println("匿名内部类复写的show");
			}
		};
		in.show();
		
		/下面的是面试题:主要面试匿名内部类和多态的知识/
		new Inter(){
			public void show() {
				System.out.println("show run...");
			}
			// 匿名内部类中的特有方法
			public void show2(){
				System.out.println("show2 run.....");
			}
		}.show2();
		/*
		 * 上面的匿名内部类,其实就是当前Inter接口的一个实现类的对象,
		 * 然后在实现接口的同时其中添加一个实现类中特有方法show2.
		 * 匿名内部类就是接口的实现类的对象,当然可以调用自己类中的特有方法,也可以调用接口中的方法
		 */
		/*
		 * 下面的代码虽然也是在实现接口,同时也定义show2的特有方法
		 * 但是最终将对象赋值给了Inter接口的类型,那么发生了多态现象
		 * 多态要求方法调用的时候,编译父类或接口中有方法,运行的是子类自己的
		 */
		Inter in2 = new Inter(){
			public void show() {
				System.out.println("show run...");
			}
			public void show2(){
				System.out.println("show2 run.....");
			}
		};
		//in2.show2();
	}
}
public class InnerClassDemom4 {
	public static void main(String[] args) {
		Outer4 out = new Outer4();
		out.func();
		out.method();
	}
}

3、枚举

3.1、介绍枚举

它是JDK5中才有的一个技术。它的出现是为了简化多例类。

多例类:一个类的对象有多个,对象的数量是有限的。

例如:写一个类表示性别。那么这个类的对象就只有2个。

/*
 * 表示性别类,他就是一个多列类
 */
public class Sex {
	private Sex(){}
	
	private static Sex famale = new Sex();
	private static Sex male = new Sex();
	
	public static Sex getFamale(){
		return famale;
	}
	public static Sex getMale(){
		return male;
	}
}

3.2、枚举类的定义

/*
 * 枚举本身就是一个类,不过需要使用enum关键字定义
 * 
 * 只要将类中使用enum关键字定义,那么它的构造方法自动就被私有了
 */
public enum Gender {
	/*
	 * 在枚举类的最前面,需要书写的是当前枚举类可以出现的所有的对象对应的引用变量名
	 */
	FAMALE, MALE;
	private int x ;
}

3.3、枚举类的构造方法传递参数

/*
 * 枚举本身就是一个类,不过需要使用enum关键字定义
 * 
 * 只要将类中使用enum关键字定义,那么它的构造方法自动就被私有了
 */
public enum Gender {
	/*
	 * 在枚举类的最前面,需要书写的是当前枚举类可以出现的所有的对象对应的引用变量名
	 */
	FAMALE("女"), MALE("男");
	private Gender(String sex){
		this.sex = sex;
	}
	private String sex ;
	
	public void printSex(){
		System.out.println(sex);
	}
}

3.4、拥有抽象方法的枚举类

/*
 * 枚举本身就是一个类,不过需要使用enum关键字定义
 * 
 * 只要将类中使用enum关键字定义,那么它的构造方法自动就被私有了
 */
public enum Gender {
	/*
	 * 在枚举类的最前面,需要书写的是当前枚举类可以出现的所有的对象对应的引用变量名
	 */
	/*
	 * 由于枚举类的构造方法全部是私有的,枚举类是没有子类的。
	 * 所以要求在创建枚举类对象的时候对当前枚举类中的抽象方法全部复写。
	 */
	FAMALE("女"){
		public void printEN() {
			System.out.println("famale");
		}
	}, 
	MALE("男"){
		public void printEN() {
			System.out.println("male");
		}
	};
	private Gender(String sex){
		this.sex = sex;
	}
	private String sex ;
	
	public void printSex(){
		System.out.println(sex);
	}
	// 枚举类中定义的抽象方法
	public abstract void printEN();
	
}

任何一个枚举类的父类都是Enum,而不是Object,但枚举类可以实现接口。

4、debug和断点调试

编程的过程中,经常需要对代码进行调试,一起写代码的过程,经常会使用输出语句来看程序在执行过程中数据变化。

debug和断点调试功能属于开发工具(IDE)自带的功能。

debug+断点调试步骤:

         1、需要在调试的变量、表达式等所在的行的最左侧双击打上断点

         2、使用debug as 的方式运行代码

         3、选择进入debug调试模式

         进入到debug中之后,程序就会停留在第一个断点的位置。        

         4、使用使用相应的快捷键或者点击图标进行程序调试

      F8  跳转到程序中的下一个断点位置,如果没有断点了,程序就运行结束

      ctrl + F2 停止程序的运行

      F5 如果程序当前在调用语句的地方,使用F5就会进入到被调用方法中。

      F7 从F5进入的方法中跳出。

      F6 程序运行到断点所在的下一行代码

在调试程序的过程中,如果遇到需要查看的变量、表达式的结果,可以选中变量、表达式,右击watch

在程序调试结束之后记得清空所有的端点和添加的表达式等信息:

最后将试图切换到相应的开发试图:

5、junit测试

测试:在软件开发完成之后,交给测试团队负责软件的测试。但是在交给测试组之前,需要自己先内测一遍。

测试分为两种:

         白盒测试:主要是针对原始代码的测试。这个要求测试的人员必须能够看懂代码,看懂其中的逻辑。白盒测试一般都是开发人员完成。

         黑盒测试:主要是功能性测试。不关注源代码。这个测试人员可以不懂技术,只要会测试即可。

junit测试:它也是开发工具自带的功能。

junit测试的步骤:

1、需要在被测试的方法上添加@Test注解。

         正常的情况下,要运行某个方法,一般都需要先创建这个类的对象,通过对象调用方法,同时程序中还需要main方法作为入口程序。

         junit测试,不需要main方法,也不需要对象,底层使用的反射技术。

2、在报错的地方,ctrl + 1 根据提示导入junit测试的jar包

3、运行代码,选中添加@Test的方法名,右击选择junit Tset测试

4、测试junit视窗

测试通过,视窗中的会是绿条。如果失败会是红条。

 junit测试的细节:

         @Test只能添加在public、非static、没有返回值,不需要参数的方法上。

	/*
 * 演示junit中的其他格式注解(@Test、@After、@Before)
 * 
 * @Test :  junit测试的启动入口
 * @After :是在@Test方法运行每次之后运行的方法
 * @Before : 是在@Test方法每次运行之前运行的方法
 * @BeforeClass: 必须放在静态的方法上,同时在是程序执行中所有@Test之前会运行的方法
 * @AfterClass: 必须放在静态的方法上,在所有@Test运行结束之后才会运行
 */
public class JunitDemo2 {
	
	@AfterClass
	public static void afterClass(){
		System.out.println("afterClass");
	}
	
	@BeforeClass
	public static void beforeClass(){
		System.out.println("beforeClass");
	}
	
	@After
	public void after(){
		System.out.println("after");
	}
	@Before
	public void before(){
		System.out.println("before");
	}
	
	@Test
	public void test1(){
		System.out.println("test1");
	}
	@Test
	public void test2(){
		System.out.println("test2");
	}
}

猜你喜欢

转载自blog.csdn.net/baozi7263/article/details/105579789
今日推荐