8.集合、泛型、枚举、注解、反射

第八章【集合、泛型、枚举、注解、反射】

一、集合

1、概述

集合是JavaAPI中提供的一种容器工具,可以用来存储多个数据。

集合框架中主要有三个要素组成:

  • 接口

  • 实现类

  • 数据结构

集合中不可以存放基本类型

集合按照其存储结构可以分为两大类:

  • java.util.Collection 单值存放
  • java.util.Map 键值存放
image-20210124094720058
2、Collection接口

Collection是父接口,其中定义了单列集合(List和Set)通用的一些方法,可用于操作所有的单列集合对象。

3、迭代器

java.util.Iterator 接口中,主要定义俩个方法:

public interface Iterator { 
    boolean hasNext();//返回当前迭代器中是否还有下一个对象 
    Object next();//获取迭代器中的下一个对象 
}
{
    //获取c1集合的迭代器对象 
    Iterator iterator = c1.iterator(); 
    //判断迭代器中,是否还有下一个元素 
    while(iterator.hasNext()){ 
        //如果有的话,就取出来 
        Object obj = iterator.next(); 
        System.out.println(obj); 
    }
}
4、foreach循环
for(变量类型 变量名 : 集合){ 
    //操作变量
}
5、数据结构

栈、队列、数组、链表、红黑树、哈希表

6、List

特点:有序、带索引、可以存放重复数据

List实现类

  1. ArrayList

    增删慢,查找快

  2. LinkedList

    增删快,查找慢;

    同时它还是一个双向链表,

  3. Vector

    内部也是采用了数组来存储数据,大多数方法都是线程安全的方法;

    查看 Vector 中方法的定义,可以看到多大数方法都使用了 synchronized 关键字,来给当前方法加锁。

7、Set

特点:无序,不带下标索引,不存放重复数据。

Set实现类:

  1. HashSet

    HashSet中存储元素是无序的,主要因为它是靠对象的哈希值来确定元素在集合中的存储位置。

    HashSet中元素不可重复,主要是靠对象的hashCode和equals方法来判断对象是否重复。

    ​ 如果俩个对象的hashCode值相等,那么再使用equals判断是否俩对象是否相同;

    ​ 如果俩个对象的hashCode值不同等,那么就不再使用equals进行判断了。

  2. TreeSet

    TreeSet可以将我们存进去的数据进行排序,排序的方式有俩种

    自然排序

    比较器排序(也称客户化排序)

思考:如果放入TreeSet中的数据既实现类自然排序又实现了客户端排序,哪个优先级高?

​ 客户端排序优先级高

8、Map

java.util.Map<K, V> 接口,就是专门处理这种映射关系数据的集合类型。

Map类型集合中,每次需要存一对数据,key-value(键值对)

  • key值必须是唯一的,value值允许重复
  • 键(key)和值(value)一一映射,一个key对应一个value
  • 在Map中,通过key值(唯一的),可以快速的找到对应的value值
  • Map不能使用迭代器遍历,因为Map没有继承Iterable接口,而迭代器方法是Iterable接口的抽象方法

Map实现类:

  1. HashMap :存储数据采用的哈希表结构,元素的存取顺序不能保证一致。由于要保证键的唯一、不重复,需

    要重写键的hashCode()方法、equals()方法,HashMap的键和值可以为空。(重要,最常用)

  2. HashTable :和之前List集合中的 Vector 的功能类似,可以在多线程环境中,保证集合中的数据的操作安全,类中的方法大多数使用了 synchronized 修饰符进行加锁,HashTable的键和值都不能为空。(线程安全)

  3. TreeMap :该类是 Map 接口的子接口 SortedMap 下面的实现类,和 TreeSet 类似,它可以对key值进行排序,同时构造器也可以接收一个比较器对象作为参数。支持key值的自然排序和比较器排序俩种方式。(支持key排序)

  4. LinkedHashMap :该类是 HashMap 的子类,存储数据采用的哈希表结构+链表结构。通过链表结构可以保证元素的存取顺序一致;(存入顺序就是取出顺序)

二、泛型

1、概述

泛型也叫做 类型参数化(Parameterized by types)

给泛型参照传的值,只能是引用类型,不能是基本类型

public class Point<T>{
    T x; 
    T y; 
}
2、集合的泛型
public interface Collection<E>{ 
    boolean add(E e); 
}
3、泛型的种类
  • 泛型类

  • 泛型接口

  • 泛型方法

4、泛型的类型

声明泛型的时候不牵扯继承

5、通配符

通配符(?)表示泛型的父类型

6、泛型的边界

如果在泛型中使用extendssuper关键字,就可以对泛型的类型进行限制。

即:规定泛型的上限下限

7、类型擦除

泛型信息被擦除后,所有的泛型类型都会统一变为原始类型:Object

三、枚举

1、介绍

枚举类是一种特殊的类,它和普通类一样可以使用构造器、定义成员变量和方法,也能实现一个或多个接口。

枚举类不能继承其他类

注意,枚举类型中的第一行代码,要求一定是指定枚举对象的个数和名字,同时最后面加分号(;)

在这行代码下, 才可以定义枚举类型的属性和方法

  1. 枚举其实也是一种类,同时还是一个final修饰的类

  2. 枚举类型都会默认继承一个父类型:java.lang.Enum,这还是一个抽象的泛型类

  3. 枚举中所定义的对象,其实就是类里面的public static final修饰的常量,并且这些常量会在静态代码块中做初始化

  4. 枚举类型中还一个默认的私有构造器,说明我们在外面并不能自己去创建枚举类型的对象

  5. 枚举类型中还有默认添加进来的方法

    values()可以返回这个枚举类型的所有对象,返回类型是数组

    valueOf(String str)通过一个字符串可以返回枚举对象,这个字符串参数就是枚举对象

  6. 枚举类型会从父类中继承过来一些方法(具体可以查看其固定的父类型)

2、定义枚举类
public enum Color {
     RED, GREEN, BLANK, YELLOW 
}
public enum Color {
     RED("红色", 1), GREEN("绿色", 2), BLANK("白色", 3), YELLO("黄色", 4);
    private String name ;
    private int index ;
    
    private Color( String name , int index ){
        this.name = name ;
        this.index = index ;
    }
     
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public int getIndex() {
        return index;
    }
    public void setIndex(int index) {
        this.index = index;
    }
}
3、获取枚举对象
	public void testEnum() {
		//1.获取枚举类对象
		Gender MAN = Gender.MAN;
		Gender WOMAN = Gender.WOMAN;
		System.out.println(MAN);
		System.out.println(WOMAN);
		//2.调用方法获取
		Gender MAN1 = Gender.valueOf("MAN");
		Gender WOMAN1 = Gender.valueOf("WOMAN");
		System.out.println(MAN1);
		System.out.println(WOMAN1);
		//3.获取全部的对象
		Gender[] genders = Gender.values();
		for (Gender gender : genders) {
			System.out.println(gender);
			gender.sex = "男;";
			System.out.println(gender.sex);
		}
	}

四、注解

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

思考:注释和注解的区别与联系

  • 注解相当于一种标记,在程序中加了注解就等于为程序打上了某种标记。程序可以利用java的反射机制来了解你的类及各种元素上有无何种标记,针对不同的标记,就去做相应的事件。

  • 注释也是一种标记,不过这种标记是为了给程序员看。

2、作用
  • 编写文档:通过代码里标识的元数据生成文档【生成文档doc文档】
  • 代码分析:通过代码里标识的元数据对代码进行分析【使用反射】
  • 编译检查:通过代码里标识的元数据让编译器能够实现基本的【编译检查】
3、内置

@Override

限定重写父类的方法,检查该方法是否为父类或者接口中重写过后的方法,如果父类或者接口中不存在该方法,则使用该注解会报错,该注解只能用于方法。

@Deprecated

用于表示某个程序元素(类,方法等)已过时

@FunctionalInterface

检查接口是否为函数式接口(jdk1.8新特性)。

@SuppressWarnings

抑制编译器警告,@SuppressWarnings修饰的程序元素(以及该程序元素中的所有子元素)取消显示指定的编译器警告。

@SuppressWarnings常见的取值:

  • unchecked:执行了未检查的转换时的警告,例如当使用集合时没有用泛型 (Generics) 来指定集合保存的类型; 关闭编译器警告

  • fallthrough:压制当 switch 程序块中没有添加break时警告;

  • path:在类路径、源文件路径等中有不存在的路径时的警告;

  • serial:当在可序列化的类上缺少 serialVersionUID 定义时的警告;

  • finally:任何 finally 子句不能正常完成时的警告;

  • all:关于以上所有情况的警告。

4、自定义
  1. 格式
[元注解];
[修饰符]  @interface 注解名称{  
    数据类型 属性名() [default  默认值];
}

public @interface MyAnno{}
  1. 本质

    自定义的注解本质是一个接口,而该接口会默认继承java.lang.annotation.Annotation,因此在注解中我们可以定义自己抽象方法。

  2. 属性

    属性的返回类型必须为以下数据类型,否则定义的属性为非法属性。

    1. 基本数据类型
    2. String类型
    3. 枚举
    4. 注解
    5. 以上数据类型对应的数组类型

    注意:属性声明成功后在使用的时候需要给属性赋值

    语法:

    @注解名称(属性名=属性值,属性名=属性值,属性名={数组类型的属性值})

    注意事项

    1. 如果定义属性时,使用了default关键字给属性进行了初始化,那么在使用时可以不用给属性进行显示赋值。
    2. 如果定义属性时只有一个属性需要赋值,且该属性名为value,那么value可以省略不写,直接书写属性值,如果需要给多个属性赋值则不能省略属性名。
    3. 如果定义的属性类型为数组类型,值需要使用{}包裹,属性值为单个值时,设置属性名={属性值}时{}可以省略。
  3. 元注解

    常用的元注解

    注解名称 注解作用
    @Retention 用于指定注解保留的阶段
    @Target 用于指定注解可以修饰程序中的哪些元素
    @Documented 注解是否会被抽取到API文档中
    @Inherited 注解是够能够被子类继承
    @Repeatable (java 8新增)

@Retention

内部包含一个value属性,返回类型为RetentionPolicy类型。

@Target

用于指定注解可以修饰程序中的哪些元素,该注解内部包含一个value属性,返回类型为ElementType[]数组类型,ElementType用于表示注解在哪些元素上可以使用。

ElementType常见的取值:

  • TYPE: 用于类,接口和枚举

  • FIELD: 用于属性

  • METHOD: 用于方法

RetentionPolicy为枚举类型,取值如下:

  • RetentionPolicy.SOURCE:Annotation只保留在源代码中,编译器编译时,直接丢弃这种Annotation,不记录在.class文件中。
  • RetentionPolicy.CLASS:编译器把Annotation记录在class文件中。当运行Java程序时,JVM中不可获取该Annotation信息。这是默认值
  • RetentionPolicy.RUNTIME:编译器把Annotation记录在class文件中。当运行Java程序时,JVM可获取该Annotation信息,程序可以通过反射获取该Annotation的信息。
5、解析

作用:Java中的注解可以用来替换项目中出现的配置文件。我们使用【Java反射】机制从一个类中解析注解。 请记住,注解保持性策略应该是RUNTIME,否则它的信息在运行期无效,我们也不能从中获取任何数据。

解析注解的过程:

  • 获取注解添加类的Class对象。

  • 通过Class对象,获取该类上面添加的注解对象。

  • 通过注解对象,获取注解对象中的属性对应的属性值。

五、反射

1、概述

反射:自己封装框架

Student类:

​ 来源:总结了所有的学生对象的特征,从中找到共同点用Java代码的形式提取出来,形成Student类。

package ……;
import ……;
[修饰符] class 类名{
    //属性
    //方法
    //构造器
}

共性:修饰符 类名 属性 方法 构造器 ----->类 Class:可以描述Java任何一个类的特征

2、Class类型

java.lang.Class是API中提供的一个类,它可以表示java中所有的类型,包括基本类型和引用类型。

在之前的学习中,我们也接触过这个类型,Object中的方法getClass方法:

public final native Class<?> getClass();

该方法的返回类型就是Class,所以obj.getClass();这句代码的含义就是:返回obj引用在运行时所指向对象的实际类型。

3、获取Class对象

在java中,每种类型(基本类型和引用类型)加载到内存之后,都会在内存中生成一个Class类型对象,这个对象就代表这个具体的java类型,并且保存这个类型中的基本信息。

注意,java中的每种类型,都有且只有唯一的一个Class类型对象与之对应!并且在类加载的时候自动生成!

  1. 获取基本类型的Class对象

    Class clazz = int.class;

  2. 获取接口类型的Class对象(两种方式)

    Class clazz1 = One.class;

    Class clazz2 = Class.forName(com.sxu.day14.One);

  3. 获取数组类型的Class对象(两种方式)

    Class clazz1 = int[].class;

    int[] arr = {1,2,3}; Class clazz2 = arr.class;

  4. 获取类类型的Class对象(三种方式)

    Class c1 = Student.class;

    Class c2 = Class.forName("com.briup.demo.Student");

    Student stu = new Student(); Class c3 = stu.getClass();

4、获取类的信息
		//获取Student类的Class对象
		Class<Student> clazz = Student.class;
		
		//从Class对象中获取Student类相关信息
		//1.查看Student在哪个包下面
		System.out.println(clazz.getPackage());
		//2.查看这个类被哪个类加载到内存中
		System.out.println(clazz.getClassLoader());
		//3.查看Student使用了哪些修饰符	public(1)+final(16)
		System.out.println(clazz.getModifiers());
		//4.查看Student使用了哪些修饰符,以字符串类型返回
		System.out.println(Modifier.toString(clazz.getModifiers()));
		//5.查看类名
		System.out.println(clazz.getName());
		System.out.println(clazz.getSimpleName());
		//6.查看实现的接口
		Class[] interfaces = clazz.getInterfaces();
		for (int i = 0; i < interfaces.length; i++) {
			System.out.println(interfaces[i]);
		}
		//7.获取类继承的父类
		System.out.println(clazz.getSuperclass());
5、反射访问属性

获取类中的public修饰的属性,也包含从父类中继承过来的public属性

  • Field[] getFields()

  • Field getField(String name)

获取类中声明的属性(包含私有的),但是不能获取从父类中继承过来的属性

  • Field[] getDeclaredFields()

  • Field getDeclaredField(String name)

		Student stu = new Student();
		Class class1 = stu.getClass();
		
		//获取类中的public修饰的属性
		Field field = class1.getField("id");
		//1.获取属性名
		System.out.println(field.getName());
		//2.获取属性类型
		System.out.println(field.getType());
		//3.获取属性的修饰符
		System.out.println(Modifier.toString(field.getModifiers()));
		//4.获取属性值
		Object object = field.get(stu);
		System.out.println(object);
		//5.设置属性值
		field.set(stu, "002");
		
		System.out.println("**********");
		//获取公共的所有的属性
//		Field[] fields = class1.getFields();
		
		//获取类中声明的属性(包含私有的)
		Field[] fields = class1.getDeclaredFields();
		for (Field ff : fields) {
			//默认私有的值取不到,获取需要设置
			ff.setAccessible(true);
			System.out.println(ff.getName());
			System.out.println(Modifier.toString(ff.getModifiers()));
			System.out.println(ff.getType().getName());
			System.out.println(ff.get(stu));
			System.out.println("----------");
		}
6、反射调用方法

获取当前类中的public方法,包含从父类中继承的public方法

  • Method[] getMethods()

  • Method getMethod(String name, Class<?>... parameterTypes)

获取当前类中声明的方法(包含私有的),但是不能获取从父类中继承过来的方法

  • Method[] getDeclaredMethods()

  • Method getDeclaredMethod(String name, Class<?>... parameterTypes)

		Student stu = new Student();
		Class class1 = stu.getClass();
		
		//获取一个方法
		Method mm = class1.getMethod("study",String.class);
		System.out.println(mm.getName());
		System.out.println(Modifier.toString(mm.getModifiers()));
		System.out.println(mm.getReturnType().getName());
		//获取参数列表
		Parameter[] parameters = mm.getParameters();
		for (Parameter pp : parameters) {
			System.out.println(pp.getName());
			System.out.println(pp.getType());
		}
		//获取方法抛出异常的个数
		int length = mm.getExceptionTypes().length;
		System.out.println(length);
		//调用方法
		mm.invoke(stu, "zs");
		
		//获取所有的方法
		Method[] methods = class1.getMethods();
7、反射创建对象

获取当前类中的public构造器

  • public Constructor<?>[] getConstructors()

  • public Constructor<T> getConstructor(Class<?>... parameterTypes)

获取当前类中的所有构造器,包含私有的

  • public Constructor<?>[] getDeclaredConstructors()

  • public Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes)

		Class clazz = Student.class;
		//获取无参构造器
		Constructor con1 = clazz.getConstructor();
		System.out.println(con1.getModifiers());
		System.out.println(con1.getName());
		System.out.println(con1.getParameterCount());
		//通过构造器构建对象
		Object object = con1.newInstance();
		System.out.println(object);
		
		//通过无参构造器创建对象,获取构造器和使用构造器可以合并为一步
		Object object1 = clazz.newInstance();
		System.out.println(object1);
		
		//正常方式
		Student student = new Student();
		System.out.println(student);
		
		//调用有参构造器
		con1 = clazz.getConstructor(String.class,String.class,double.class,double.class);
		Object object2 = con1.newInstance("001","ls",99.0,66.3);
		System.out.println(object2);	
8、反射获取注解
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotataion{
	public String className();
	public String methodName();
}
public class MyAnnotationTest {
	@MyAnnotataion(className = "com.sxu.day15.MyAnnotationTest",methodName = "test")
	public static void test() {
		System.out.println("测试注解");
	}
}
	@Test
	public void testAnnotation() throws Exception {
		Class clazz = MyAnnotationTest.class;
		Method mm = clazz.getMethod("test");
		MyAnnotataion aa = mm.getAnnotation(MyAnnotataion.class);
		String methodName = aa.methodName();
		String className = aa.className();
		System.out.println(methodName);
		System.out.println(className);
		System.out.println(mm.isAnnotationPresent(MyAnnotataion.class));
		System.out.println("**********");
		Class clazz1 = Class.forName(className);
		Object oo = clazz1.newInstance();
		clazz1.getMethod(methodName).invoke(oo);
	}
posted @ 2024-04-14 23:52  可乐爱兑姜汁  阅读(3)  评论(0编辑  收藏  举报