JavaWeb之Java基础知识增强

Posted 达少Rising

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了JavaWeb之Java基础知识增强相关的知识,希望对你有一定的参考价值。

1.JUnit测试

  • 黑盒测试:只关注输入是否对应正确的输出,不需要写代码
  • 白盒测试:关注代码内部的实现细节,需要写代码

1.1 不用JUnit测试

  • 传统测试案例中使用main方法测试代码的正确性
package com.weeks.junit;

/**
 * 一个实体类
 */
public class Calculator 
    public int add(int a, int b)
        return a + b;
    
    
    public int sub(int a, int b)
        return a - b;
    


使用main方法测试

package com.weeks.junit;

/**
 * 测试类
 */
public class CalculatorTest 
    public static void main(String[] args) 
        //新建一个实体类对象
        Calculator calculator = new Calculator();
        //测试add方法
//        int result = calculator.add(3, 5);
        //测试sub方法
        int result = calculator.sub(5, 3);
        System.out.println(result);
    

这种传统的测试方法的缺点很明显:就是在main方法中测试时为方法之间不互相影响就使用注释或者使用不同的变量名称测试不同的方法,使得代码的测试逻辑在别人看来很不清晰。为了解决这个问题可以使用JUnit。

1.2使用JUnit测试

引入JUnit单元测试,属于白盒测试
步骤:

  • 定义一个测试类(测试用例),约定熟成的规则
    • 测试类名:被测试的类名Test,CalculatorTest
    • 包名:xxx.xxx.xx.test,com.weeks.test
  • 定义测试方法:可以独立运行
  • 给方法加上@Test注解
  • 导入JUnit依赖环境
package com.weeks.test;

import com.weeks.junit.Calculator;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;

public class CalculatorTest 

    /**
     * 测试add方法
     */
    @Test
    public void testAdd()
        Calculator calculator = new Calculator();
        int result = calculator.add(3, 5);
//        System.out.println(calculator.add(3, 5));
		//一般测试中很少使用输出作为测试结果,一般使用断言希望结果与测试结果是否一致
        Assert.assertEquals(8, result);
        System.out.println("testAdd...");
    


  • 判定结果
    • 红色:失败
    • 绿色:成功
    • 一般会使用断言来处理结果:Assert.assertEquals(期望的结果, 运算的结果)
  • 补充
    • @Before:修饰的方法会在测试方法之前被自动执行
    • @After:修饰的方法在测试方法执行之后自定被执行
package com.weeks.test;

import com.weeks.junit.Calculator;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;

public class CalculatorTest 

    /**
     * 初始化方法
     * 所有方法在执行之前执行init方法
     */
    @Before
    public void init()
        System.out.println("init...");
    

    /**
     * 关闭资源的方法
     * 在所有方法执行之后执行close方法
     */
    @After
    public void close()
        System.out.println("close...");
    
    /**
     * 测试add方法
     */
    @Test
    public void testAdd()
        Calculator calculator = new Calculator();
        int result = calculator.add(3, 5);
//        System.out.println(calculator.add(3, 5));
        Assert.assertEquals(8, result);
        System.out.println("testAdd...");
    

执行的结果是:

init...
testAdd...
close...

2.反射

2.1 反射的概念

要了解反射就要了解Java程序运行的三个阶段

  • 反射就是将类的各个组成部分封装为其他对象。反射是框架设计的灵魂
  • 框架是一种半成品的软件,可以在框架的基础上进行软件开发,简化编码
  • 反射的好处:
    • 可以在程序运行过程中操作这些对象
    • 可以解藕,提高程序的可扩展性

2.2 获取Class对象的方式

  • Class.forName(“全类名”):将字节码文件加载进内存,返回Class对象
    • 多用于配置文件,将类名定义在配置文件中。读取文件,加载类
  • 类名.class:通过类名的属性class获取
    • 多用于参数的传递
  • 对象.getClass():getClass()方法在Object类中定义着。
    • 多用于对象的获取字节码的方式
package com.weeks.refection.domain;

/**
 * 一个实体类
 */
public class Person 
    private String name;
    private int age;

    public Person() 
    

    public Person(String name, int age) 
        this.name = name;
        this.age = age;
    

package com.weeks.refection.domain;

/**
 * 反射获取对象
 */
public class RefectionDemo 
    public static void main(String[] args) throws Exception 
        //反射获取Class对象的三种方法
        //1.Class.forName(全类名)
        Class cls1 = Class.forName("com.weeks.refection.domain.Person");
        System.out.println(cls1);
        //2.类名.class
        Class cls2 = Person.class;
        System.out.println(cls2);
        //3.对象.getClass()
        Person person = new Person();
        Class cls3 = person.getClass();
        System.out.println(cls3);

        //比较三个对象是否相等
        System.out.println(cls1 == cls2);
        System.out.println(cls1 == cls3);
    

结果

class com.weeks.refection.domain.Person
class com.weeks.refection.domain.Person
class com.weeks.refection.domain.Person
true
true
  • 结论:同一个字节码文件(*.class)在一次程序运行过程中,只会被加载一次,不论通过哪一种方式获取的Class对象都是同一个。

2.3 Class对象功能

  • 获取功能:
    • 获取成员变量们

      • Field[] getFields() :获取所有public修饰的成员变量

      • Field getField(String name) 获取指定名称的 public修饰的成员变量

      • Field[] getDeclaredFields() 获取所有的成员变量,不考虑修饰符

      • Field getDeclaredField(String name)

    • 获取构造方法们

      • Constructor<?>[] getConstructors()

      • Constructor getConstructor(类<?>… parameterTypes)

      • Constructor getDeclaredConstructor(类<?>… parameterTypes)

      • Constructor<?>[] getDeclaredConstructors()

    • 获取成员方法们:

      • Method[] getMethods()

      • Method getMethod(String name, 类<?>… parameterTypes)

      • Method[] getDeclaredMethods()

      • Method getDeclaredMethod(String name, 类<?>… parameterTypes)

    • 获取全类名

      • String getName()
  • Field:成员变量

    • 操作:
      • 设置值: void set(Object obj, Object value)
      • 获取值: get(Object obj)
      • 忽略访问权限修饰符的安全检查: setAccessible(true):暴力反射
  • Constructor:构造方法

    • 创建对象:
      • T newInstance(Object… initargs)

      • 如果使用空参数构造方法创建对象,操作可以简化:Class对象的newInstance方法

  • Method:方法对象

    • 执行方法:
      • Object invoke(Object obj, Object… args)
  • 获取方法名称:
    • String getName:获取方法名
package com.weeks.refection;

import com.weeks.refection.domain.TestClassObject;
import org.junit.Test;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;

public class RefectionDemo2 

    //成员变量们
    @Test
    public void classGetFiles() throws Exception 
        //获取Class对象
        Class testClassObjectClass = TestClassObject.class;
        //1.getField()获取单个Class成员变量,只能获取public修饰的成员变量
        Field m_pub = testClassObjectClass.getField("m_pub");
        System.out.println(m_pub);
        //1.1.获取成员变量的两个目的就是获取值和设置值
        TestClassObject testClassObject = new TestClassObject();
        //获取值
        Object m_pub_value = m_pub.get(testClassObject);
        System.out.println(m_pub_value);
        //设置值
        m_pub.set(testClassObject, "public_value");
        System.out.println(m_pub.get(testClassObject));
        System.out.println("========================");

        //2.getFields()获取多个成员变量,也是只能获取public修饰成员变量
        Field[] fields = testClassObjectClass.getFields();
        for(Field field : fields)
            System.out.println(field);
        
        System.out.println("========================");

        //3.getDeclaredField()获取private修饰的成员变量
        Field m_pri = testClassObjectClass.getDeclaredField("m_pri");
        //设置私有成员变量的值,注意先进行暴力反射后才能设置
        m_pri.setAccessible(true);//暴力反射
        m_pri.set(testClassObject, "private_value");
        System.out.println(m_pri.get(testClassObject));
        System.out.println("========================");

        //4.getDeclaredFields()获取所有的成员变量,包括私有成员变量
        Field[] declaredFields = testClassObjectClass.getDeclaredFields();
        for (Field field : declaredFields)
            System.out.println(field);
        
    

    //构造方法们
    @Test
    public void classGetConstructor() throws Exception 
        //获取Class对象
        Class<TestClassObject> testClassObjectClass = TestClassObject.class;
        //1.getConstructor()获取单个public修饰的构造方法
        Constructor<TestClassObject> csPublic = testClassObjectClass.getConstructor(String.class);
        //1.1.获取构造方法的目的就是创建变量
        TestClassObject testClassObject1 = csPublic.newInstance("m_pub");
        System.out.println(testClassObject1);
        System.out.println("=====================");

        //2.getConstructors()获取所有public修饰的构造方法
        Constructor<?>[] constructors = testClassObjectClass.getConstructors();
        for (Constructor constructor : constructors)
            System.out.println(constructor);
        
        System.out.println("=====================");

        //3.获取private修饰的构造方法
        Constructor<TestClassObject> dcsPrivate = testClassObjectClass.getDeclaredConstructor(String.class, String.class, String.class, String.class);
        dcsPrivate.setAccessible(true);//需要暴力反射
        TestClassObject testClassObject2 = dcsPrivate.newInstance("m_pub", "m_pro", "m_def", "m_pri");
        System.out.println(testClassObject2);
        System.out.println("=====================");

        //4.获取所有构造方法
        Constructor<?>[] declaredConstructors = testClassObjectClass.getDeclaredConstructors();
        for(Constructor constructor : declaredConstructors)
            System.out.println(constructor);
        
    

    //成员方法们
    @Test
    public void classGetMethod() throws Exception 
        //获取Class对象
        Class<TestClassObject> testClassObjectClass = TestClassObject.class;
        //1.getMethod()获取单个public修饰的成员方法
        //1.1.无参
        Method publicMethod = testClassObjectClass.getMethod("publicMethod");
        //调用成员方法
        TestClassObject testClassObject = new TestClassObject();
        publicMethod.invoke(testClassObject);
        //1.2.有参
        Method publicMethodWithParam = testClassObjectClass.getMethod("publicMethod", String.class);
        publicMethodWithParam.invoke(testClassObject, "我是一个String类型的参数");
        System.out.println("=====================");

        //2.getMethods()获取所有public修饰的成员方法,包括父类的public方法
        Method[] methods = testClassObjectClass.getMethods();
        for (Method method : methods)
            System.out.println(method);
        
        System.out.println("=====================");

        //3.getDeclaredMethod()获取单个私有成员方法
        Method privateMethod = testClassObjectClass.getDeclaredMethod("privateMethod");
        privateMethod.setAccessible(true);//需要暴力反射
        privateMethod.invoke(testClassObject);
        System.out.println("=====================");

        //4.获取所有成员方法,包括父类的方法
        Method[] declaredMethods = testClassObjectClass.getDeclaredMethods();
        for (Method method : declaredMethods)
            System.out.println(method);
        
    

    //获取类名
    @Test
    public void classGetClassName()
        //获取Class对象
        Class<TestClassObject> testClassObjectClass = TestClassObject.class;
        //获取类名
        String className = testClassObjectClass.getName();

        System.out.println(className);
    

2.4 案例:

  • 需求:写一个"框架",不能改变该类的任何代码的前提下,可以帮我们创建任意类的对象,并且执行其中任意方法
    • 实现:
      • 配置文件
      • 反射
    • 步骤:
      • 将需要创建的对象的全类名和需要执行的方法定义在配置文件中
      • 在程序中加载读取配置文件
      • 使用反射技术来加载类文件进内存
      • 创建对象
      • 执行方法
package com.weeks.refection.domain;

public class Student 
    public void learning()
        System.out.println("正在学习");
    

package com.weeks.refection.domain;

public class Teacher 
    public void teaching()
        System.out.println("正在教学");
    

通过该配置文件改变需要加载的类和方法

className=com.weeks.refection.domain.Student
methodName=learning
className=com.weeks.refection.domain.Teacher
methodName=teaching
package com.weeks.refection;

import java.io.InputStream;
import java.lang.reflect.Method;
import java.util.Properties;

/**
 * 框架类,在不改变源代码的前提下,创建任意类的实例对象
 * 调用创建类实例对象的方法
 */
public class RefectionFrameDemo 
    public static void main(String[] args) throws Exception 
        //1.将需要创建的对象的全类名和需要执行的方法定义在配置文件中
        //2.使用加载配置文件,通过类加载器和properties
        ClassLoader classLoader = RefectionFrameDemo.class.getClassLoader();
        InputStream is = classLoader.getResourceAsStream("pro.properties");
        Properties properties = new Properties();
        properties.load(is);
        String className = (String) properties.get("className");
        String methodName = (String) properties.get("methodName");

        //3.使用反射技术来加载文件进内存
        Class<?> aClass = Class.forName(className);
        //4.创建对象
        Object instance = aClass.newInstance();
        //5.获取方法执行方法
        Method method = aClass.getMethod(methodName);以上是关于JavaWeb之Java基础知识增强的主要内容,如果未能解决你的问题,请参考以下文章

JavaWeb之Java基础知识增强

JavaWeb知识汇总目录

JavaWeb知识汇总目录

JavaWeb之Ajax&JSON

JavaWeb之redis&Jedis

JavaWeb之Bootstrap