九、Java基础部分强化(Junit单元测试、反射 、注解、抽象类、接口)

一 Junit单元测试:

1. 测试分类:

  • 黑盒测试:不需要写代码,给输入值,看程序是否能够输出期望的值。
  • 白盒测试:需要写代码的。关注程序具体的执行流程。
⑴ Junit使用:白盒测试
① 步骤:
  • 定义一个测试类(测试用例)

    建议
     - 测试类名:被测试的类名+Test   如:CalculatorTest
     - 包名:xxx.xxx.xx.test   如:com.kejizheantan.test

  • 定义测试方法:可以独立运行

    建议
      - 方法名:test测试的方法名     如:testAdd()
      - 返回值:void
      - 参数列表:空参

  • 给方法加@Test

  • 导入junit依赖环境

代码如下
Calculator计算器类:

/**
 * 计算器类
 */
public class Calculator {
    /**
     * 加法
     */
    public int add (int a , int b){
        //int i = 3/0;

        return a + b;
    }
    /**
     * 减法
     */
    public int sub (int a , int b){
        return a - b;
    }
}

测试类:

public class CalculatorTest {
    @Test
    public void addTest(){
        Calculator calc = new Calculator();
        int num = calc.add(2, 5);
        System.out.println(num);
    }
}

执行结果:
在这里插入图片描述

注意:
 1.要加注解@Test并导入import org.junit.Test;依赖
 2.执行的方式如下图
在这里插入图片描述

② 判定结果:
  • 红色:失败
    在这里插入图片描述

  • 绿色:成功
    在这里插入图片描述

    一般我们会使用断言操作来处理结果
       Assert.assertEquals(期望的结果,运算的结果);
    代码如下:
    在这里插入图片描述
    结果如下:
    在这里插入图片描述

③ 补充:
  • @Before:
    修饰的方法会在测试方法之前被自动执行
  • @After:
    修饰的方法会在测试方法执行之后自动被执行

代码如下:
JunitTest类:

public class JunitTest {
    public void show(){
        System.out.println("junit测试.....");
    }
}

测试代码TestDemo类:

public class TestDemo {
    /**
     * 初始化方法:
     *  用于资源申请,所有测试方法在执行之前都会先执行该方法
     */
    @Before
    public void init(){
        System.out.println("用于初始化资源的方法init....");
    }
    /**
    *测试方法
    */
    @Test
    public void showTest(){
        JunitTest jt = new JunitTest();
        jt.show();
    }
    /**
     * 释放资源方法:
     *  在所有测试方法执行完后,都会自动执行该方法
     */
    @After
    public void close(){
        System.out.println("用于关闭资源的方close...");
    }
}

结果如下:
在这里插入图片描述

二 反射:框架设计的灵魂

⑴ 框架:

 半成品软件。可以在框架的基础上进行软件开发,简化编码

⑵ 反射

将类的各个组成部分封装为其他对象,这就是反射机制

  • java代码的三个阶段:
    在这里插入图片描述

好处

  • 可以在程序运行过程中,操作这些对象。
  • 可以解耦,提高程序的可扩展性。
① 获取Class对象的方式
  1. Class.forName("全类名")将字节码文件加载进内存,返回Class对象(第一阶段的获取方式)
    多用于配置文件,将类名定义在配置文件中。读取文件,加载类
  2. 类名.class通过类名的属性class获取*(第二阶段获取方式)
    多用于参数的传递
  3. 对象.getClass()getClass()方法在Object类中定义着。(第三阶段的获取方式)
    多用于对象的获取字节码的方式

代码如下:

  • Person类:

    public class Person {
        private String name;
        private int age;
    
        public String a;
        protected String b;
        String c;
        private String d;
    
    
        public Person() {
        }
    
        public Person(String name, int age) {
    
            this.name = name;
            this.age = age;
        }
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public int getAge() {
            return age;
        }
    
        public void setAge(int age) {
            this.age = age;
        }
    
        @Override
        public String toString() {
            return "Person{" +
                    "name='" + name + '\'' +
                    ", age=" + age +
                    ", a='" + a + '\'' +
                    ", b='" + b + '\'' +
                    ", c='" + c + '\'' +
                    ", d='" + d + '\'' +
                    '}';
        }
    
    
        public void eat(){
            System.out.println("eat...");
        }
    
        public void eat(String food){
            System.out.println("eat..."+food);
        }
    }
    
    
  • ReflectDemo类

    public class ReflectDemo {
        /**
            获取Class对象的方式:
                1. Class.forName("全类名"):将字节码文件加载进内存,返回Class对象
                2. 类名.class:通过类名的属性class获取
                3. 对象.getClass():getClass()方法在Object类中定义着。
         */
        public static void main(String[] args) throws Exception {
    
            //1.Class.forName("全类名")
            Class person1 = Class.forName("com.kejizhentan.demo8.Person");
            System.out.println(person1);
            //2.类名.class
            Class person2  = Person.class;
            System.out.println(person2);
            //3.对象.getClass()
            Class person3 = new Person().getClass();
            System.out.println(person3);
            //== 比较三个对象
            System.out.println(person1==person2);//true
            System.out.println(person2 == person3);//true
        }
    }
    

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

② Class对象功能:

获取功能

  1. 获取成员变量们
    Field[] getFields():获取所有public修饰的成员变量
    Field getField(String name): 获取指定名称的 public修饰的成员变量

    Field[] getDeclaredFields() 获取所有的成员变量,不考虑修饰符
    Field getDeclaredField(String name) 获指定名称的成员变量,不考虑修饰

    代码如下
    Person类:

    public class Person {
    private String name;
    private int age;
    
    //定义四种不同修饰符下的成员变量
    public String a;
    protected String b;
    String c;
    private String d;
    
    
    public Person() {
    }
    
    public Person(String name, int age) {
    
        this.name = name;
        this.age = age;
    }
    
    public String getName() {
        return name;
    }
    
    public void setName(String name) {
        this.name = name;
    }
    
    public int getAge() {
        return age;
    }
    
    public void setAge(int age) {
        this.age = age;
    }
    
    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", a='" + a + '\'' +
                ", b='" + b + '\'' +
                ", c='" + c + '\'' +
                ", d='" + d + '\'' +
                '}';
    }
    
    
    public void eat(){
        System.out.println("eat...");
    }
    
    public void eat(String food){
        System.out.println("eat..."+food);
    }
    }
    

    ReflectDemo类:

    /**
     Class对象功能:
     * 获取功能:
         1. 获取成员变量们
             * Field[] getFields()
             * Field getField(String name)
    
             * Field[] getDeclaredFields()
             * Field getDeclaredField(String name)
     */
    public class ReflectDemo {
    public static void main(String[] args) throws Exception {
        //0.获取Person的Class对象
        Class p = Person.class;
        //通过Field[] getFields()获取所有被public修饰的成员属性
        Field[] fields = p.getFields();
        for (Field field : fields) {
            System.out.println("通过Field[] getFields()获取所有被public修饰的成员属性:"+field);
        }
    
        //通过Field getField(String name)获取指定名称的被public修饰的成员属性
        Field a = p.getField("a");
        System.out.println("通过Field getField(String name)获取指定名称的被public修饰的成员属性:"+a);
    
        //通过Field[] getDeclaredFields()获取所有的成员属性(包括public、省略、protected、private修饰的成员属性)
        Field[] fs = p.getDeclaredFields();
        for (Field f : fs) {
            System.out.println("通过Field[] getDeclaredFields()获取所有的成员属性(包括public、省略、protected、private修饰的成员属性):"+f);
        }
        //通过Field getDeclaredField(String name)获取指定名称的成员属性(包括public、省略、protected、private修饰的成员属性)
        Field d = p.getDeclaredField("d");
        System.out.println("通过Field getDeclaredField(String name)获取指定名称的成员属性(包括public、省略、protected、private修饰的成员属性):"+d);
     }
    }
    

    结果如下:
    在这里插入图片描述

  2. 获取构造方法们
    Constructor<?>[] getConstructors()获取所有public修饰的构造方法(包括有参和无参)
    Constructor<T> getConstructor(类<?>... parameterTypes) 获取指定参数类型并用public修饰的构造方法
    Constructor<T> getDeclaredConstructor(类<?>... parameterTypes)获取指定参数类型的构造方法,不考虑修饰符
    Constructor<?>[] getDeclaredConstructors()获取所有构造方法,不考虑修饰符(包括有参和无参)

    代码如下:

    Person类:

    public class Person {
        private String name;
        private int age;
    
        //定义四种不同修饰符下的成员变量
        public String a;
        protected String b;
        String c;
        private String d;
    
        //定义四种不同修饰符下的构造方法
        public Person(String a, String b) {
    
        }
        protected Person(String a, String b,int age) {
    
        }
        Person(String a,int age,String b) {
    
        }
        private Person(int age,String b) {
    
        }
    
    
    
        public Person() {
        }
    
        public Person(String name, int age) {
    
            this.name = name;
            this.age = age;
        }
    
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public int getAge() {
            return age;
        }
    
        public void setAge(int age) {
            this.age = age;
        }
    
        @Override
        public String toString() {
            return "Person{" +
                    "name='" + name + '\'' +
                    ", age=" + age +
                    ", a='" + a + '\'' +
                    ", b='" + b + '\'' +
                    ", c='" + c + '\'' +
                    ", d='" + d + '\'' +
                    '}';
        }
    
    
        public void eat(){
            System.out.println("eat...");
        }
    
        public void eat(String food){
            System.out.println("eat..."+food);
        }
    }
    

    ReflectDemo类:

    public class ReflectDemo {
        public static void main(String[] args) throws Exception {
            Class p = Person.class;
            //通过Constructor<?>[] getConstructors()获取所有的构造方法(包括有参和无参)
            Constructor[] constructors = p.getConstructors();
            for (Constructor constructor : constructors) {
                System.out.println("通过Constructor<?>[] getConstructors()获取所有的构造方法(包括有参和无参):"+constructor);
            }
            //通过Constructor<T> getConstructor(<?>... parameterTypes)获取指定参数类型并由public修饰的的构造方法
            Constructor constructor = p.getConstructor(String.class,int.class);
            System.out.println("通过Constructor<T> getConstructor(类<?>... parameterTypes)获取指定参数类型并由public修饰的的构造方法:"+constructor);
    
            //Constructor<T> getDeclaredConstructor(<?>... parameterTypes)  获取指定参数类型的构造方法,不考虑修饰符
            Constructor con = p.getDeclaredConstructor(int.class, String.class);
            System.out.println("Constructor<T> getDeclaredConstructor(类<?>... parameterTypes) 获取指定参数类型的构造方法,不考虑修饰符"+con);
            //Constructor<?>[] getDeclaredConstructors()  获取所有构造方法,不考虑修饰符(包括有参和无参)
            Constructor[] cons = p.getDeclaredConstructors();
            for (Constructor con1 : cons) {
                System.out.println("Constructor<?>[] getDeclaredConstructors()  获取所有构造方法,不考虑修饰符(包括有参和无参)"+con1);
            }
        }
    }
    

    结果如下:
    在这里插入图片描述

  3. 获取成员方法们:
    Method[] getMethods() 获取所有public修饰的方法(包含了Object类和父类中的方法)
    Method getMethod(String name, 类<?>... parameterTypes) 获取指定方法名和参数,并被public修饰的方法
    Method[] getDeclaredMethods() 获取所有的方法,不考虑修饰符(不包含Object类和父类中的方法)
    Method getDeclaredMethod(String name, 类<?>... parameterTypes) 获取指定方法名和参数,不考虑修饰符

    代码如下:

    Person类:

    public class Person {
        private String name;
        private int age;
    
        //定义四种不同修饰符下的成员变量
        public String a;
        protected String b;
        String c;
        private String d;
    
        //定义四种不同修饰符下的构造方法
        public Person(String a, String b) {
    
        }
        protected Person(String a, String b,int age) {
    
        }
        Person(String a,int age,String b) {
    
        }
        private Person(int age,String b) {
    
        }
    
    
    
        public Person() {
        }
    
        public Person(String name, int age) {
    
            this.name = name;
            this.age = age;
        }
    
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public int getAge() {
            return age;
        }
    
        public void setAge(int age) {
            this.age = age;
        }
    
        @Override
        public String toString() {
            return "Person{" +
                    "name='" + name + '\'' +
                    ", age=" + age +
                    ", a='" + a + '\'' +
                    ", b='" + b + '\'' +
                    ", c='" + c + '\'' +
                    ", d='" + d + '\'' +
                    '}';
        }
    
    
        public void eat(){
            System.out.println("eat...");
        }
    
        public void eat(String food){
            System.out.println("eat..."+food);
        }
    
        private void eat(int num){
            System.out.println("eat了..."+num+"个");
        }
    }
    

    ReflectDemo类:

    /**
     Method[] getMethods()  获取所有public修饰的方法(包含了Object类和父类中的方法)
     Method getMethod(String name, 类<?>... parameterTypes)  获取指定方法名和参数,并被public修饰的方法
     Method[] getDeclaredMethods() 获取所有的方法,不考虑修饰符(包含了Object类和父类中的方法)
     Method getDeclaredMethod(String name, 类<?>... parameterTypes)  获取指定方法名和参数,不考虑修饰符
     */
    public class ReflectDemo {
        public static void main(String[] args) throws Exception {
            Class p = Person.class;
            // Method[] getMethods()  获取所有public修饰的方法(包含了Object类和父类中的方法)
            Method[] methods = p.getMethods();
            for (Method method : methods) {
                System.out.println(" Method[] getMethods()  获取所有public修饰的方法(包含了Object类和父类中的方法)" + method);
            }
            //Method getMethod(String name, 类<?>... parameterTypes)  获取指定方法名和参数,并被public修饰的方法
            Method eat = p.getMethod("eat",String.class);
            System.out.println("Method getMethod(String name, 类<?>... parameterTypes)  获取指定方法名和参数,并被public修饰的方法:"+eat);
    
            //Method[] getDeclaredMethods() 获取所有的方法,不考虑修饰符(不包含Object类和父类中的方法)
            Method[] ms = p.getDeclaredMethods();
            for (Method method : ms) {
                System.out.println("Method[] getDeclaredMethods() 获取所有的方法,不考虑修饰符(不包含Object类和父类中的方法):"+method);
            }
            // Method getDeclaredMethod(String name, 类<?>... parameterTypes)  获取指定方法名和参数,不考虑修饰符
            Method eatMethod = p.getDeclaredMethod("eat", int.class);
            System.out.println(" Method getDeclaredMethod(String name, 类<?>... parameterTypes)  获取指定方法名和参数,不考虑修饰符:" +eatMethod);
        }
    }
    

    结果如下:
    在这里插入图片描述

  4. 获取全类名
    String getName()

    代码如下:
    Student类:

    public class Student {
    
    }
    

    ReflectDemo类

    public class ReflectDemo {
        public static void main(String[] args) throws Exception {
            Class studentClass = Student.class;
            System.out.println("类的全名称是:"+studentClass.getName());
        }
    }
    

    结果如下:
    在这里插入图片描述

③ Field:成员变量

操作

  1. 设置值
    void set(Object obj, Object value)
  2. 获取值
    get(Object obj)
  3. 忽略访问权限修饰符的安全检查
    setAccessible(true):暴力反射

代码如下:
Student类:

public class Student {
    private String name;
    private int age;
    public String sex;
    public Student() {
    }

    public Student(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Student student = (Student) o;
        return age == student.age &&
                Objects.equals(name, student.name);
    }

    @Override
    public int hashCode() {
        return Objects.hash(name, age);
    }

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", sex='" + sex + '\'' +
                '}';
    }
}

ReflectDemo类:

public class ReflectDemo {
    public static void main(String[] args) throws Exception {
        Class studentClass = Student.class;
        //一般成员变量值的获取和设置
        Field sex = studentClass.getField("sex");
        //获取成员变量sex的值
        Student student = new Student();
        Object s = sex.get(student);
        System.out.println("Student中sex变量的值为:"+s);
        //设置成员变量sex的值
        sex.set(student,"男");
        System.out.println("赋值完后Student对象为:"+student);

        //private修饰的成员变量值的获取和设置
        Class stu = Student.class;
        Field field = stu.getDeclaredField("name");
        /*获取成员变量name
        Student st = new Student();
        Object n = name.get(st);
        System.out.println("private修饰的成员变量为:"+n);//抛:"Exception in thread "main" java.lang.IllegalAccessException: class com.kejizhentan.demo8.ReflectDemo cannot access a member of class com.kejizhentan.demo8.Student with modifiers "private"
	at java.base/jdk.internal.reflect.Reflection.newIllegalAccessException(Reflection.java:361)"异常*/
        //获取成员变量name
        Student st = new Student();
        //忽略访问权限修饰符的安全检查
        field.setAccessible(true);//暴力反射
        Object n = field.get(st);
        System.out.println("private修饰的成员变量为:"+n);
    }
}
④ Constructor:构造方法

T newInstance(Object... initargs) :创建对象
如果使用空参数构造方法创建对象,操作可以简化:Class对象的newInstance方法

代码如下:
student类:

public class Student {
    private String name;
    private int age;
    public String sex;
    public Student() {
    }

    public Student(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Student student = (Student) o;
        return age == student.age &&
                Objects.equals(name, student.name);
    }

    @Override
    public int hashCode() {
        return Objects.hash(name, age);
    }

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", sex='" + sex + '\'' +
                '}';
    }
}

ReflectDemo类:

public class ReflectDemo {
    public static void main(String[] args) throws Exception {
        Class studentClass = Student.class;
        //获取有参构造方法
        Constructor constructor = studentClass.getConstructor(String.class, int.class);
        //创建对象
        Object student = constructor.newInstance("张三",18);
        System.out.println("通过有参构建方法创建的对象为:"+student);

        //获取空参构造方法
        Constructor con = studentClass.getConstructor();
        //创建对象
        Object o = con.newInstance();
        System.out.println("通过空参构建方法创建的对象为:" + o);

        //使用空参构造方法创建对象的简写版
        Object o1 = studentClass.newInstance();
        System.out.println("使用空参构造方法创建对象的简写版:"+o1);
    }
}
⑤ Method:方法对象
  • Object invoke(Object obj, Object... args) :执行方法
  • String getName:获取方法名

代码如下
Student类

public class Student {
    public void eat(){
        System.out.println("吃饭的方法!!!");
    }
}

ReflectDemo类

public class ReflectDemo {
    public static void main(String[] args) throws Exception {
        Class studentClass = Student.class;
        //获取指定名称的方法
        Method eat = studentClass.getMethod("eat");
        //获取方法名
        String methodName = eat.getName();
        System.out.println("方法名为:"+methodName);
        Student student = new Student();
        //执行方法
        eat.invoke(student);
    }
}

结果如下
在这里插入图片描述

注意:
  上述的成员变量、成员方法、构造器都能通过setAccessible(true)暴力反射获取private修饰的类成员

⑶案例:

需求:写一个"框架",不能改变该类的任何代码的前提下,可以帮我们创建任意类的对象,并且执行其中任意方法

  • 实现:
    1. 配置文件
    2. 反射
  • 步骤:
    1. 将需要创建的对象的全类名和需要执行的方法定义在配置文件中
    2. 在程序中加载读取配置文件
    3. 使用反射技术来加载类文件进内存
    4. 创建对象
    5. 执行方法

代码如下:
项目结构:
在这里插入图片描述
配置文件:

className=com.kejizhentan.demo9.Student
methodName=eat

Student类:

public class Student {
    public void eat(){
        System.out.println("吃饭的方法!!!");
    }
}

ReflectTest类:

/**
 * 框架类
 */
public class ReflectTest {
    public static void main(String[] args) throws Exception {
        //可以创建任意类的对象,可以执行任意方法

        /*
            前提:不能改变该类的任何代码。可以创建任意类的对象,可以执行任意方法
         */
        //1.加载配置文件
        //1.1创建Properties对象
        Properties properties = new Properties();
        //1.2加载配置文件,转换为一个集合
        //1.2.1获取class目录下的配置文件
        ClassLoader classLoader = ReflectTest.class.getClassLoader();
        InputStream is = classLoader.getResourceAsStream("pro.properties");
        properties.load(is);

        //2.获取配置文件中定义的数据
        String className = properties.getProperty("className");
        String methodName = properties.getProperty("methodName");

        //3.加载该类进内存
        Class cls  = Class.forName(className);
        //4.创建对象
        Object obj = cls.newInstance();
        //5.获取方法对象
        Method method = cls.getMethod(methodName);
        //6.执行方法
        method.invoke(obj);
    }
}

结果如下:
在这里插入图片描述

三 注解

⑴ 注解与注释的区别:

  • 注解:说明程序的。给计算机看的
  • 注释:用文字描述程序的。给程序员看的

⑵ 定义:

注解Annotation),也叫元数据。一种代码级别的说明。它是JDK1.5及以后版本引入的一个特性,与类、接口、枚举是在同一个层次。它可以声明在包、类、字段、方法、局部变量、方法参数等的前面,用来对这些元素进行说明,注释。

概念描述

  • JDK1.5之后的新特性
  • 说明程序的
  • 使用注解:@注解名称

⑶ 作用分类

①编写文档:

  通过代码里标识的注解生成文档【生成文档doc文档】

  • 编写代码加上相应的注解说明(如:@Description@param@return等)
    文件名称如下:
    在这里插入图片描述
    代码如下:

    /**
        * @Description:    通过代码里标识的注解生成文档
        * @Author:         kejizhentan
        * @CreateDate:     2021/08/28
        * @UpdateUser:     kejizhentan
        * @Version:        1.0
        * @since           java1.5之后
    */
    public class AnnotationDemo {
        /**
            * @Description:计算两数的和
            * @param a 整数
            * @param b 整数
            * @return 返回两数的和
        */
        public int add(int a,int b){
            return a+b;
        }
    }
    
  • 将编写好的.AnnotationDemo.java文件放到想生成doc文档的文件夹中,并编辑AnnotationDemo.java文件,将上方导包的语句删掉
    在这里插入图片描述

    在这里插入图片描述

  • 执行javadoc AnnotationDemo.java命令,生成api文文档
    在这里插入图片描述

  • 生成的html如下
    在这里插入图片描述
    在这里插入图片描述

②代码分析:

  通过代码里标识的注解对代码进行分析【使用反射】

③编译检查:

  通过代码里标识的注解让编译器能够实现基本的编译检查【Override】
在这里插入图片描述

⑷ JDK中预定义的一些注解

① @Override :
检测被该注解标注的方法是否是继承自父类(接口)的
② @Deprecated:
该注解标注的内容,表示已过时

在这里插入图片描述

③ @SuppressWarnings:
压制警告
一般传递参数all  @SuppressWarnings("all")

在这里插入图片描述
在这里插入图片描述

⑸ 自定义注解

① 格式:

public @interface 注解名称{
  属性列表;
}

本质:注解本质上就是一个接口,该接口默认继承Annotation接口
public interface MyAnno extends java.lang.annotation.Annotation {}

② 属性(指注解中的抽象方法)

要求:

  1. 属性的返回值类型有下列取值
    * 基本数据类型
    * String
    * 枚举
    * 注解
    * 以上类型的数组
    如:
    在这里插入图片描述在这里插入图片描述
    在这里插入图片描述

  2. 定义了属性,在使用时需要给属性赋值
    * 如果定义属性时,使用default关键字给属性默认初始化值,则使用注解时,可以不进行属性的赋值。
    如:
    在这里插入图片描述
    在这里插入图片描述
    * 如果只有一个属性需要赋值,并且属性的名称是value,则value可以省略,直接定义值即可。
    在这里插入图片描述
    在这里插入图片描述
    * 数组赋值时,值使用{}包裹。如果数组中只有一个值,则{}可以省略
    如:
    在这里插入图片描述在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

⑹ 元注解:用于描述注解的注解

  • @Target:描述注解能够作用的位置

    ElementType取值:
    *TYPE:可以作用于类上
    在这里插入图片描述
    在这里插入图片描述
    *METHOD:可以作用于方法上
    *FIELD:可以作用于成员变量上
    在这里插入图片描述
    在这里插入图片描述

  • @Retention:描述注解被保留的阶段
    @Retention(RetentionPolicy.RUNTIME):当前被描述的注解,会保留到class字节码文件中,并被JVM读取到

  • @Documented:描述注解是否被抽取到api文档中

  • @Inherited:描述注解是否被子类继承

⑺ 综合案例

① 使用注解的方式解决反射案例中的问题

需求:写一个"框架",不能改变该类的任何代码的前提下,可以帮我们创建任意类的对象,并且执行其中任意方法
项目结构
在这里插入图片描述
Student类:

public class Student {
    public void eat(){
        System.out.println("吃饭的方法!!!");
    }
}

Pro自定义注解

/**
 * 描述需要执行的类名,和方法名
 */
@Target({ElementType.TYPE})//表示该注解只能标注在类上
@Retention(RetentionPolicy.RUNTIME)//当前被描述的注解,会保留到class字节码文件中,并被JVM读取到
public @interface Pro {
    String className();
    String methodName();
}

ReflectTest类

/**
 * 框架类
 */
@Pro(className = "com.kejizhentan.demo10.Student",methodName = "eat")
public class ReflectTest {
    public static void main(String[] args) throws Exception {
        /*
            前提:不能改变该类的任何代码。可以创建任意类的对象,可以执行任意方法
         */
        //1.解析注解
        //1.1获取该类的字节码文件对象
        Class<ReflectTest> reflectTestClass = ReflectTest.class;
        //2.获取上边的注解对象
        //其实就是在内存中生成了一个该注解接口的子类实现对象
        /* public class ProImpl implements Pro{
            public String className(){
                return "com.kejizhentan.demo10.Student";
            }
            public String methodName(){
                return "eat";
            }

        }*/
        Pro an = reflectTestClass.getAnnotation(Pro.class);
        //3.调用注解对象中定义的抽象方法,获取返回值
        String className = an.className();
        String methodName = an.methodName();
        //System.out.println(className);
        //System.out.println(methodName);
        //3.加载该类进内存
        Class cls = Class.forName(className);
        //4.创建对象
        Object obj = cls.newInstance();
        //5.获取方法对象
        Method method = cls.getMethod(methodName);
        //6.执行方法
        method.invoke(obj);
    }
}

结果如下:
在这里插入图片描述

② 简单的测试框架

项目结构
在这里插入图片描述
自定义注解Check

@Retention(RetentionPolicy.RUNTIME)//当前被描述的注解,会保留到class字节码文件中,并被JVM读取到
@Target(ElementType.METHOD)//当前被描述的注解只能标注在方法上
public @interface Check {
}

计算器类Calculator

/**
 * 小明定义的计算器类
 */
public class Calculator {

    //加法
    @Check
    public void add(){
        System.out.println("1 + 0 =" + (1 + 0));
    }
    //减法
    @Check
    public void sub(){
        System.out.println("1 - 0 =" + (1 - 0));
    }
    //乘法
    @Check
    public void mul(){
        System.out.println("1 * 0 =" + (1 * 0));
    }
    //除法
    @Check
    public void div(){
        System.out.println("1 / 0 =" + (1 / 0));
    }
    public void show(){
        System.out.println("永无bug...");
    }
}

TestCheck类

/**
 * 简单的测试框架
 * 当主方法执行后,会自动自行被检测的所有方法(加了Check注解的方法),判断方法是否有异常,记录到文件中
 */
public class TestCheck {
    public static void main(String[] args) throws IOException {
        //1.创建计算器对象
        Calculator c = new Calculator();
        //2.获取字节码文件对象
        Class cls = c.getClass();
        //3.获取所有方法
        Method[] methods = cls.getMethods();
        int number = 0;//出现异常的次数
        BufferedWriter bw = new BufferedWriter(new FileWriter("bug.txt"));
        for (Method method : methods) {
            //4.判断方法上是否有Check注解
            if(method.isAnnotationPresent(Check.class)){
                //5.有,执行
                try {
                    method.invoke(c);
                } catch (Exception e) {
                    //6.捕获异常
                    //记录到文件中
                    number ++;
                    bw.write(method.getName()+ " 方法出异常了");
                    bw.newLine();
                    bw.write("异常的名称:" + e.getCause().getClass().getSimpleName());
                    bw.newLine();
                    bw.write("异常的原因:"+e.getCause().getMessage());
                    bw.newLine();
                    bw.write("--------------------------");
                    bw.newLine();

                }
            }
        }
        bw.write("本次测试一共出现 "+number+" 次异常");
        bw.flush();
        bw.close();
    }
}

执行结果
1.生成测试报告文件
在这里插入图片描述
在这里插入图片描述
2.控制台打印结果如下
在这里插入图片描述

  • 小结:
    1. 以后大多数时候,我们会使用注解,而不是自定义注解
    2. 注解给谁用?
     * 编译器
     *给解析程序用
    3. 注解不是程序的一部分,可以理解为注解就是一个标签

四、抽象类

⑴ 概述

父类中的方法,被它的子类们重写,子类各自的实现都不尽相同。那么父类的方法声明和方法主体,只有声明还有 意义,而方法主体则没有存在的意义了。我们把没有方法主体的方法称为抽象方法。Java语法规定,包含抽象方法 的类就是抽象类。

⑵ 定义抽象

  • 抽象方法 : 没有方法体的方法。
  • 抽象类:包含抽象方法的类。

⑶ abstract使用格式

①抽象方法

使用 abstract 关键字修饰方法,该方法就成了抽象方法,抽象方法只包含一个方法名,而没有方法体。
定义格式

修饰符 abstract 返回值类型 方法名 (参数列表);

代码举例

public abstract void run();
② 抽象类

如果一个类包含抽象方法,那么该类必须是抽象类。
定义格式:

abstract class 类名字 {
}

代码举例:

public  abstract  class Abstractdemo {
    public abstract void run();
}
③ 抽象的使用

继承抽象类的子类必须重写父类所有的抽象方法。否则,该子类也必须声明为抽象类。最终,必须有子类实现该父 类的抽象方法,否则,从最初的父类到最终的子类都不能创建对象,失去意义。
Animal抽象类:

public  abstract class Animal {
    public abstract void run();
}

Cat子类

public class Cat extends Animal {
    @Override
    public void run() {
        System.out.println("小猫在墙头走~~~");
    }
}

测试类:

public class CatTest {
    public static void main(String[] args) {
        // 创建子类对象
        Cat c = new Cat();
        // 调用run方法
        c.run();
    }
}

结果如下:

在这里插入图片描述
此时的方法重写,是子类对父类抽象方法的完成实现,我们将这种方法重写的操作,也叫做实现方法

⑷ 注意事项

关于抽象类的使用,以下为语法上要注意的细节,虽然条目较多,但若理解了抽象的本质,无需死记硬背。

  1. 抽象类不能创建对象,如果创建,编译无法通过而报错。只能创建其非抽象子类的对象。

    理解:假设创建了抽象类的对象,调用抽象的方法,而抽象方法没有具体的方法体,没有意义。

  2. 抽象类中,可以有构造方法,是供子类创建对象时,初始化父类成员使用的。

    理解:子类的构造方法中,有默认的super(),需要访问父类构造方法。

  3. 抽象类中,不一定包含抽象方法,但是有抽象方法的类必定是抽象类。

    理解:未包含抽象方法的抽象类,目的就是不想让调用者创建该类对象,通常用于某些特殊的类结构设 计。

  4. 抽象类的子类,必须重写抽象父类中所有的抽象方法,否则,编译无法通过而报错。除非该子类也是抽象 类。

    理解:假设不重写所有抽象方法,则类中可能包含抽象方法。那么创建对象后,调用抽象的方法,没有 意义。

五、接口

⑴ 概述

接口,是Java语言中一种引用类型,是方法的集合,如果说类的内部封装了成员变量、构造方法和成员方法,那么 接口的内部主要就是封装了方法,包含抽象方法(JDK 7及以前),默认方法和静态方法(JDK 8),私有方法 (JDK 9)。
接口的定义,它与定义类方式相似,但是使用interface 关键字。它也会被编译成.class文件,但一定要明确它并 不是类,而是另外一种引用数据类型

引用数据类型:数组,类,接口。

接口的使用,它不能创建对象,但是可以被实现(implements ,类似于被继承)。一个实现接口的类(可以看做 是接口的子类),需要实现接口中所有的抽象方法,创建该类对象,就可以调用方法了,否则它必须是一个抽象 类。

⑵定义格式

public interface 接口名称 {
  // 抽象方法
   // 默认方法
  // 静态方法
  // 私有方法
}

① 含有抽象方法

抽象方法:使用abstract 关键字修饰,可以省略,没有方法体。该方法供子类实现使用。
代码如下:

public interface InterFaceName { 
	public abstract void method(); 
}
② 含有默认方法和静态方法

默认方法:使用default 修饰,不可省略,供子类调用或者子类重写。
静态方法:使用 static 修饰,供接口直接调用。
代码如下:

public interface InterFaceName {
    public default void method() {
        // 执行语句
    }
    public static void method2() {
        // 执行语句
    }
}
③ 含有私有方法和私有静态方法

私有方法:使用 private修饰,供接口中的默认方法或者静态方法调用。

public interface InterFaceName {
    private void method() {
        // 执行语句
    }
    private static  void method2(){
       // 执行语句 
    }
}

⑶ 基本的实现

实现的概述
类与接口的关系为实现关系,即类实现接口,该类可以称为接口的实现类,也可以称为接口的子类。实现的动作类 似继承,格式相仿,只是关键字不同,实现使用 implements关键字。
非抽象子类实现接口:

  1. 必须重写接口中所有抽象方法。
  2. 继承了接口的默认方法,即可以直接调用,也可以重写。

实现格式:

class 类名 implements 接口名 {
   // 重写接口中抽象方法【必须】
   // 重写接口中默认方法【可选】
}

① 抽象方法的使用

必须全部实现,代码如下:
定义接口:

public interface InterFaceName {
    public abstract void eat();
    public abstract void work();
}

定义实现类:

public class ImplementsClass implements InterFaceName {
    @Override
    public void eat() {
        System.out.println("饿了,就得吃饭啊!!!");
    }
    @Override
    public void work() {
        System.out.println("上班真特么类!!!");
    }

定义测试类:

public class TestDemo {
    public static void main(String[] args) {
        ImplementsClass ic = new ImplementsClass();
        ic.eat();
        ic.work();
    }
}

结果如下:
在这里插入图片描述

② 默认方法的使用

可以继承,可以重写,二选一,但是只能通过实现类的对象来调用。代码如下:
定义接口:

public interface InterFaceName {
    //默认方法可以继承,可以重写,二选一,但是只能通过实现类的对象来调用。
    public default void sleep() {
        System.out.println("真想天天睡到自然醒啊!!!");
    }
    public default void copyDemo() {
        System.out.println("复制粘贴代码!!!");
    }
}

定义实现类:

public class ImplementsClass implements InterFaceName {
    @Override
    public  void sleep(){
        System.out.println("默认方法可以不重写!!!");
    }
}

定义测试类

public class TestDemo {
    public static void main(String[] args) {
        ImplementsClass ic = new ImplementsClass();
        ic.copyDemo();
        ic.sleep();
    }
}

结果如下:
在这里插入图片描述

③ 静态方法的使用

静态与.class 文件相关,只能使用接口名调用,不可以通过实现类的类名或者实现类的对象调用,代码如下:
定义接口:

public interface InterFaceName {
    public static void run(){
        System.out.println("跑起来~~~");
    }
}

定义实现类:

public class ImplementsClass implements InterFaceName {
    // 无法重写静态方法
}

定义测试类

public class TestDemo {
    public static void main(String[] args) {
        // ImplementsClass.run(); // 【错误】无法继承方法,也无法调用
        InterFaceName.run();
    }
}

结果如下:
在这里插入图片描述

④ 私有方法的使用
  • 私有方法:只有默认方法可以调用。
  • 私有静态方法:默认方法和静态方法可以调用。

如果一个接口中有多个默认方法,并且方法中有重复的内容,那么可以抽取出来,封装到私有方法中,供默认方法 去调用。从设计的角度讲,私有的方法是对默认方法和静态方法的辅助。同学们在已学技术的基础上,可以自行测 试。
定义接口:

public interface InterFaceName {
    default void func() {
        //私有方法只有默认方法可以调用
        func1();
        //私有静态方法,默认方法和静态方法都可以调用
        func2();
    }
    public   static  void method(){
        //私有静态方法,默认方法和静态方法可以调用
        func2();
    }
    private void func1() {
        System.out.println("私有方法跑起来~~~");
    }

    private static void func2() {
        System.out.println("私有静态方法跑起来~~~");
    }
}

定义实现类:

public class ImplementsClass implements InterFaceName {
    // 无法重写静态方法
}

定义测试类

public class TestDemo {
    public static void main(String[] args) {
        ImplementsClass ic = new ImplementsClass();
        ic.func();//通过实现类,调用接口的默认方法
        // ImplementsClass.method(); // 【错误】无法继承方法,也无法调用
        InterFaceName.method();
    }
}

执行结果如下:

在这里插入图片描述

⑷ 接口的多实现

之前学过,在继承体系中,一个类只能继承一个父类。而对于接口而言,一个类是可以实现多个接口的,这叫做接 口的多实现。并且,一个类能继承一个父类,同时实现多个接口。
实现格式:

class 类名 [extends 父类名] implements 接口名1,接口名2,接口名3… {
  // 重写接口中抽象方法【必须】
   // 重写接口中默认方法【不重名时可选】
}
[ ]: 表示可选操作。

① 抽象方法

接口中,有多个抽象方法时,实现类必须重写所有抽象方法。如果抽象方法有重名的,只需要重写一次。代码如 下:
定义多个接口:

public interface A {
    public abstract void showA();
    public abstract void show();
}
public interface B {
    public abstract void showB();
    public abstract void show();
}

实现类C

public class C implements A,B {
    @Override
    public void showA() {
        System.out.println("方法showA被调用了!");
    }

    @Override
    public void showB() {
        System.out.println("方法showB被调用了!");
    }

    @Override
    public void show() {
        System.out.println("A和B重名方法被调用了!");
    }
}

测试类:

public class TestDemo {
    public static void main(String[] args) {
        C c = new C();
        c.showA();
        c.showB();
        c.show();
    }
}
② 默认方法

接口中,有多个默认方法时,实现类都可继承使用。**如果默认方法有重名的,必须重写一次。**代码如下:
定义多个接口:

public interface A {
    public default void methodA(){
        System.out.println("A接口中的默认方法methodA被执行了!");
    }
    public default void method(){
        System.out.println("A接口中的默认方法method被执行了!");
    }
}
public interface B {
    public default void methodB(){
        System.out.println("B接口中的默认方法methodB被执行了!");
    }
    public default void method(){
        System.out.println("B中的默认方法method被执行了!");
    }
}

实现类C

public class C implements A,B {
    @Override
    public void method() {
        //如果默认方法有重名的,必须重写一次
        System.out.println("被重写的method方法被调用了!");
    }
}

测试类:

public class TestDemo {
    public static void main(String[] args) {
        C c = new C();
        c.methodA();
        c.methodB();
        c.method();
    }
}

执行结果如下:
在这里插入图片描述

③ 静态方法

接口中,存在同名的静态方法并不会冲突,原因是只能通过各自接口名访问静态方法。
定义多个接口:

public interface A {
    public static void methodA(){
        System.out.println("A接口中的静态方法methodA被执行了!");
    }
    public static void method(){
        System.out.println("A接口中的静态方法method被执行了!");
    }
}
public interface B {
    public static void methodB(){
        System.out.println("B接口中的静态方法methodB被执行了!");
    }
    public static void method(){
        System.out.println("B中的静态方法method被执行了!");
    }
}

实现类C

public class C implements A,B {
    // 无法重写静态方法
}

测试类:

public class TestDemo {
    public static void main(String[] args) {
        // C.method(); // 【错误】无法继承方法,也无法调用
        A.method();
        A.methodA();
        B.method();
        B.methodB();
    }
}

执行结果如下:
在这里插入图片描述

⑸ 优先级的问题

当一个类,既继承一个父类,又实现若干个接口时,父类中的成员方法与接口中的默认方法重名,子类就近选择执 行父类的成员方法。代码如下:
定义接口:

public interface A {
    public default void methodA(){
        System.out.println("接口A中的methodA方法被调用了!!!");
    }
}

定义父类:

public class B {
    public void methodA(){
        System.out.println("父类B中的methodA方法被调用了!!!");
    }
}

定义子类:

public class C extends B implements A {
    // 未重写methodA方法
}

测试类:

public class TestDemo {
    public static void main(String[] args) {
        C c = new C();
        c.methodA();
    }
}

⑹ 接口的多继承(了解内容)

一个接口能继承另一个或者多个接口,这和类之间的继承比较相似。接口的继承使用 extends 关键字,子接口继 承父接口的方法。如果父接口中的默认方法有重名的,那么子接口需要重写一次。代码如下:
定义父接口:

public interface A {
    public default void method(){
        System.out.println("父接口A");
    }
}
public interface B {
    public default void method(){
        System.out.println("父接口B");
    }
}

定义子接口:

public interface C extends B , A {
    @Override
    public default void method() {
        System.out.println("子接口重写的方法method");
    }
}

定义实现类:

public class D implements C {
    @Override
    public void method() {
        System.out.println("实现类重写了接口C中的method方法");
    }
}

定义测试类:

public class TestDemo {
    public static void main(String[] args) {
        D d = new D();
        d.method();
    }
}

执行结果如下:
在这里插入图片描述

小贴士:
子接口重写默认方法时,default关键字可以保留。
子类重写默认方法时,default关键字不可以保留。

⑺ 其他成员特点

  • 接口中,无法定义成员变量,但是可以定义常量,其值不可以改变,默认使用public static final修饰。
  • 接口中,没有构造方法,不能创建对象。
  • 接口中,没有静态代码块。
public interface A {
   // public String name;无法定义成员变量
    public static final String NAME = "zhangsan";//可以定义常量,其值不可以改变,默认使用public static final修饰。
    //public A(){}接口中,没有构造方法,不能创建对象。
    /*static {
       接口中,没有静态代码块
    }*/
}
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值