第八章【集合、泛型、枚举、注解、反射】
一、集合
1、概述
集合是JavaAPI中提供的一种容器工具,可以用来存储多个数据。
集合框架中主要有三个要素组成:
-
接口
-
实现类
-
数据结构
集合中不可以存放基本类型
集合按照其存储结构可以分为两大类:
java.util.Collection
单值存放java.util.Map
键值存放
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实现类
-
ArrayList
增删慢,查找快
-
LinkedList
增删快,查找慢;
同时它还是一个双向链表,
-
Vector
内部也是采用了数组来存储数据,大多数方法都是线程安全的方法;
查看 Vector 中方法的定义,可以看到多大数方法都使用了
synchronized
关键字,来给当前方法加锁。
7、Set
特点:无序,不带下标索引,不存放重复数据。
Set实现类:
-
HashSet
HashSet中存储元素是无序的,主要因为它是靠对象的哈希值来确定元素在集合中的存储位置。
HashSet中元素不可重复,主要是靠对象的hashCode和equals方法来判断对象是否重复。
如果俩个对象的hashCode值相等,那么再使用equals判断是否俩对象是否相同;
如果俩个对象的hashCode值不同等,那么就不再使用equals进行判断了。
-
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实现类:
-
HashMap
:存储数据采用的哈希表结构,元素的存取顺序不能保证一致。由于要保证键的唯一、不重复,需要重写键的hashCode()方法、equals()方法,HashMap的键和值可以为空。(重要,最常用)
-
HashTable
:和之前List集合中的 Vector 的功能类似,可以在多线程环境中,保证集合中的数据的操作安全,类中的方法大多数使用了 synchronized 修饰符进行加锁,HashTable的键和值都不能为空。(线程安全) -
TreeMap
:该类是 Map 接口的子接口 SortedMap 下面的实现类,和 TreeSet 类似,它可以对key值进行排序,同时构造器也可以接收一个比较器对象作为参数。支持key值的自然排序和比较器排序俩种方式。(支持key排序) -
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、泛型的边界
如果在泛型中使用extends
和super
关键字,就可以对泛型的类型进行限制。
即:规定泛型的上限和下限。
7、类型擦除
泛型信息被擦除后,所有的泛型类型都会统一变为原始类型:Object
三、枚举
1、介绍
枚举类是一种特殊的类,它和普通类一样可以使用构造器、定义成员变量和方法,也能实现一个或多个接口。
枚举类不能继承其他类
注意,枚举类型中的第一行代码,要求一定是指定枚举对象的个数和名字,同时最后面加分号(;)
在这行代码下, 才可以定义枚举类型的属性和方法
-
枚举其实也是一种类,同时还是一个final修饰的类
-
枚举类型都会默认继承一个父类型:
java.lang.Enum
,这还是一个抽象的泛型类 -
枚举中所定义的对象,其实就是类里面的public static final修饰的常量,并且这些常量会在静态代码块中做初始化
-
枚举类型中还一个默认的私有构造器,说明我们在外面并不能自己去创建枚举类型的对象
-
枚举类型中还有默认添加进来的方法
values()
可以返回这个枚举类型的所有对象,返回类型是数组valueOf(String str)
通过一个字符串可以返回枚举对象,这个字符串参数就是枚举对象 -
枚举类型会从父类中继承过来一些方法(具体可以查看其固定的父类型)
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、自定义
- 格式
[元注解];
[修饰符] @interface 注解名称{
数据类型 属性名() [default 默认值];
}
public @interface MyAnno{}
-
本质
自定义的注解本质是一个接口,而该接口会默认继承
java.lang.annotation.Annotation
,因此在注解中我们可以定义自己抽象方法。 -
属性
属性的返回类型必须为以下数据类型,否则定义的属性为非法属性。
- 基本数据类型
- String类型
- 枚举
- 注解
- 以上数据类型对应的数组类型
注意:属性声明成功后在使用的时候需要给属性赋值
语法:
@注解名称(属性名=属性值,属性名=属性值,属性名={数组类型的属性值})
注意事项
- 如果定义属性时,使用了
default
关键字给属性进行了初始化,那么在使用时可以不用给属性进行显示赋值。 - 如果定义属性时只有一个属性需要赋值,且该属性名为
value
,那么value
可以省略不写,直接书写属性值,如果需要给多个属性赋值则不能省略属性名。 - 如果定义的属性类型为数组类型,值需要使用{}包裹,属性值为单个值时,设置
属性名={属性值}
时{}可以省略。
-
元注解
常用的元注解
注解名称 注解作用 @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类型对象与之对应!并且在类加载的时候自动生成!
-
获取基本类型的Class对象
Class clazz = int.class;
-
获取接口类型的Class对象(两种方式)
Class clazz1 = One.class;
Class clazz2 = Class.forName(com.sxu.day14.One);
-
获取数组类型的Class对象(两种方式)
Class clazz1 = int[].class;
int[] arr = {1,2,3}; Class clazz2 = arr.class;
-
获取类类型的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);
}
标签:System,public,println,枚举,泛型,注解,Class,out
From: https://www.cnblogs.com/pengdali/p/18134945