JAVA初步学习——第八章 集合、泛型、枚举、反射

一、集合

1.概述

集合和数组之间的区别有:

  • 数组的长度是固定的,集合的长度是可变的
  • 数组中存储的是同一类型的元素,集合中存储的数据可以是不同类型的
  • 数组中可以存放基本类型数据或者对象,集合中只能存放对象
  • 数组是由JVM中现有的 类型+[] 组合而成的,除了一个length属性,还有从Object中继承过来的方法之外,数组对象就调用不到其他属性和方法了
  • 集合是由JavaAPI中的java.util包里面所提供的接口和实现类组成的,这里面定义并实现了很多方法,可以使用集合对象直接调用这些方法,从而操作集合存放的数据

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

  • 接口
    • 整个集合框架的上层结构,都是用接口进行组织的。
    • 接口中定义了集合中必须要有的基本方法。
    • 通过接口还把集合划分成了几种不同的类型,每一种集合都有自己对应的接口。
  • 实现类
    • 对于上层使用接口划分好的集合种类,每种集合的接口都会有对应的实现类。
    • 每一种接口的实现类很可能有多个,每个的实现方式也会各有不同。
  • 数据结构
    • 每个实现类都实现了接口中所定义的最基本的方法,例如对数据的存储、检索、操作等方法。
    • 但是不同的实现类,它们存储数据的方式不同,也就是使用的数据结构不同。

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

  • java.util.Collection
  • java.util.Map

2.Collection接口

Collection接口是单列集合类的父接口,这种集合可以将数据一个一个的存放到集合中。它有两个重要的子接口,分别是 java.util.Listjava.util.Set
Collection类型集合必须要有的基本的方法

//向集合中添加元素 
boolean add(E e) 

//把一个指定集合中的所有数据,添加到当前集合中 
boolean addAll(Collection<? extends E> c) 

//清空集合中所有的元素。 
void clear() 

//判断当前集合中是否包含给定的对象。 
boolean contains(Object o) 

//判断当前集合中是否包含给定的集合的所有元素。 
boolean containsAll(Collection<?> c) 

//判断当前集合是否为空。 
boolean isEmpty() 

//返回遍历这个集合的迭代器对象 
Iterator<E> iterator() 

//把给定的对象,在当前集合中删除。 
boolean remove(Object o) 

//把给定的集合中的所有元素,在当前集合中删除。 
boolean removeAll(Collection<?> c) 

//判断俩个集合中是否有相同的元素,如果有当前集合只保留相同元素,如果没有当前集合元素清空 
boolean retainAll(Collection<?> c) 

//返回集合中元素的个数。 
int size() 

//把集合中的元素,存储到数组中。 
Object[] toArray() 

//把集合中的元素,存储到数组中,并指定数组的类型 
<T> T[] toArray(T[] a)

3.迭代器

(1)API中提供了一个迭代器接口: java.util.Iterator该接口可以很方便的迭代出集合中的元素。
(2)java.util.Collection 接口继承了 java.lang.Iterable 接口
Collection 接口及其子接口中,都有一个获取迭代器对象的方法: Iterator iterator();
(3)java.util.Iterator 接口中,主要定义俩个方法:

public interface Iterator { 
	boolean hasNext();//返回当前迭代器中是否还有下一个对象 
	Object next();//获取迭代器中的下一个对象 
}

4. foreach循环

Collection类型及其子类型的集合,还有数组,都可以使用foreach循环进行遍历其中的元素数据
格式:

for(变量类型 变量名 : 集合){ 
	//操作变量 
}

(1)遍历集合

public static void main(String[] args) { 
	Collection<String> c1 = new ArrayList<>(); 
	c1.add("hello1"); 
	c1.add("hello2"); 
	c1.add("hello3");
	for(Object o:c1){ 
		System.out.println(o); 
	} 
}

(2)遍历数组

public static void main(String[] args) { 
	int[] arr = {1,3,5,7,9}; 
	//每次循环,使用变量i接收数组中的一个数据
	for(int i : arr){ 
		System.out.println(i); 
	} 
}

5.数据结构

数据存储的常用结构有:

  • 队列
  • 数组
  • 链表
  • 红黑树
  • 哈希表

(1)(例:桶装薯片)
栈(stack),又称堆栈,仅允许在栈的一端进行插入和删除操作,并且不允许在其他任何位置进行操作。
栈的入口、出口的都是栈的顶端位置
其特点是:先进后出,最先存进去的元素,最后才能取出来。

入栈也称为压栈,把数据存入到栈的顶端位置
出栈也称为弹栈,把栈顶位置的数据取出

(2)队列(例:火车穿过山洞)
队列(queue),仅允许在队列的一端进行插入,而在队列的另一端进行删除。
队列的入口、出口分别在队列的俩端
其特点是:先进先出,最先存进去的元素,可以最先取出来。
(3)数组
数组(array),内存中一块连续的空间,元素数据在其中按照下标索引依次存储,比较常用的数据结构。
数组查询元素较快,中间位置的插入、删除元素较慢。
其特点是:通过下标索引,可以快速访问指定位置的元素,但是在数组中间位置添加数据或者删除数据会比较慢,因为数组中间位置的添加和删除元素,为了元素数据能紧凑的排列在一起,那么就会引起其后面的元素移动位置。
(4)链表
链表(linked list),是有一个一个node节点组成,每个node节点中存储了一个数据,以及一个指向下一个node节点对象的引用(单向链表),如果是双向链表的话,还会存储另一个引用,指向了上一个node节点对象。
head表示链表的头部,tail表示链表的尾部
其特点是:

  • 查找元素慢,因为需要通过连接的节点,依次向后查找指定元素(没有直接的下标索引)
  • 新增和删除元素较快,例如删除,只需要让当前node节点中的引用指向另一个节点对象即可,原来的指向的node节点就相当于删除了。

(5)红黑树
二叉树中有一种叫做红黑树(Red/Black Tree),它最早被称为平衡二叉B树(symmetric binary B- trees),后来被称为红黑树。
红黑树是一种特殊化的平衡二叉树,它可以在进行插入和删除的时候,如果左右子数的高度相差较大,那么就通过特定操作(左旋、右旋)保持二叉查找树的平衡(动态平衡),从而获得较高的查找性能。
红黑树的每一个节点的左子树的所有数据都比自己小,而右子树的所有数据都比自己大,并且左右子树的高度近似

红黑树的约束:

  1. 根节点必须是黑色
  2. 其他节点可以是红色的或者黑色
  3. 叶子节点(特指null节点)是黑色的
  4. 每个红色节点的子节点都是黑色的
  5. 任何一个节点到其每一个叶子节点的所有路径上黑色节点数相同

(6)哈希表

  • 在JDK1.8之前是采用数组+链表进行实现,根据数据的哈希值,把数据存在数组中,但是当前哈希值冲突的时候,再使用链表进行存储,那么在数组中,同一hash值的数据都存在一个链表里。
  • JDK1.8中,哈希表存储采用数组+链表+红黑树进行实现,当链表长度超过阈值(8)时,将链表转换为红黑树,这样可以大大提高查找的性能。

6.List集合

java.util.List 接口继承了 Collection 接口,是常用的一种集合类型。
List集合的特点:有序可重复,并且可以使用下标索引进行访问

List 集合具有 Collection 集合的特点之外,还具有自己的一些特点:

  • List是一种有序的集合
    例如,向集合中存储的元素顺序是8、2、5。那么集合中就是按照这个顺序进行存储的
  • List是一种带索引的集合
    可以通过元素的下标索引,精确查找对应的元素数据
  • List是一种可以存放重复数据的集合
    可以把相同的数据,在List集合中多次保存
//返回集合中指定位置的元素。 
E get(int index); 

//用指定元素替换集合中指定位置的元素,并返回被替代的旧元素。 
E set(int index, E element); 

//将指定的元素,添加到该集合中的指定位置上。 
void add(int index, E element); 

//从指定位置开始,把另一个集合的所有元素添加进来 
boolean addAll(int index, Collection<? extends E> c); 

//移除列表中指定位置的元素, 并返回被移除的元素。 
E remove(int index); 

//查收指定元素在集合中的所有,从前往后查到的第一个元素(List集合可以重复存放数据) 
int indexOf(Object o); 

//查收指定元素在集合中的所有,从后往前查到的第一个元素(List集合可以重复存放数据) 
int lastIndexOf(Object o); 

//根据指定开始和结束位置,截取出集合中的一部分数据
List<E> subList(int fromIndex, int toIndex);

7.List接口实现类

List接口常用的的实现类有三个:

  • ArrayList
  • LinkedList
  • Vector
  • ArrayList(最常用的集合)
    java.util.ArrayList 是最常用的一种List类型集合, ArrayList 类中使用数组来实现数据的存储
    它的特点是就是:增删慢,查找快
  • LinkedList
    java.util.LinkedList 存储数据采用的数据结构是链表
    它的特点是:增删快,查找慢
    (1)LinkedList 同时还是一个双向链表,除了头节点(head)和尾节点(tail)之外,其他节点都含有俩个引用,一个引用指向它的上一个节点,另一个引用指向它的的下一个节点
    (2)在 LinkedList 中,定义了一些操作头节点和尾节点的方法:
//将指定元素插入此列表的开头 
void addFirst(E e) 

//将指定元素添加到此列表的结尾 
void addLast(E e) 

//返回此列表的第一个元素 
E getFirst() 

//返回此列表的最后一个元素 
E getLast() 

//从此列表所表示的堆栈处弹出一个元素 
E pop() 

//将元素推入此列表所表示的堆栈 
void push(E e) 

//移除并返回此列表的第一个元素
E removeFirst() 

//移除并返回此列表的最后一个元素 
E removeLast()

(3)LinkedList 类不仅实现了List接口,还有 Queue 接口以及子接口 Deque 也实现了

Queue 是队列接口, Deque 是双端队列

(4) LinkedList 集合也可以作为栈和队列的数据结构来使用

JavaAPI中提供了 java.util.Stack 来实现栈结构,但官方目前已不推荐使用,而是推荐使用 java.util.Deque双端队列来实现队列与栈的各种需求
LinkedList 同时具有队列和栈的操作方法,pop和push都是栈结构的操作方法

Queue 中方法和 Deque 中方法对比:

QueueDeque说明
add(e)addLast(e)向队尾插入元素,失败则抛出异常
offer(e)offerLast(e)向队尾插入元素,失败则返回false
remove()removeFirst()获取并删除队首元素,失败则抛出异常
poll()pollFirst()获取并删除队首元素,失败则返回null
element()getFirst()获取但不删除队首元素,失败则抛出异常
peek()peekFirst()获取但不删除队首元素,失败则返回null

Stack 中方法和 Deque 中方法对比:

StackDeque说明
push(e)addFirst(e)向栈顶插入元素,失败则抛出异常
offerFirst(e)向栈顶插入元素,失败则返回false
pop()removeFirst()获取并删除栈顶元素,失败则抛出异常
pollFirst()获取并删除栈顶元素,失败则返回null
peek()peekFirst()获取但不删除栈顶元素,失败则返回null
  • Vector
    (1)Vector 内部也是采用了数组来存储数据,但是 Vector 中的方法大多数都是线程安全的方法
    (2)在多线并发访问的环境中,可以使用 Vector 来保证集合中元据操作的安全。
    (3)查看 Vector 中方法的定义,可以看到多大数方法都使用了 synchronized 关键字,来给当前方法加锁。

8.Set集合

java.util.Set 接口继承了 Collection 接口,是常用的一种集合类型。
Set类型集合的特点:无序、不可重复

Set 集合具有 Collection 集合的特点之外,还具有自己的一些特点:

  • Set是一种无序的集合
  • Set是一种不带下标索引的集合
  • Set是一种不能存放重复数据的集合

9.Set实现类

Set接口常用的的实现类有两个:

  • HashSet
  • TreeSet(TreeSet是Set接口的子接口SortedSet的实现类)
  • HashSet
    (1)java.util.HashSet 类的实现,主要依靠的是 HashMap
    (2)HashSet中存储元素是无序的,主要因为它是靠对象的哈希值来确定元素在集合中的存储位置
    (3)HashSet中元素不可重复,主要是靠对象的hashCodeequals方法来判断对象是否重复
    (4)HashSet并没有把nameage都相同的对象,当做相同的对象进行去重
  • HashSet中判断对象是否重复是根据对象的hashCode值和equals的比较结果,而不是根据对象的name和age值是否相等!
    • 如果俩个对象的hashCode相等,那么再使用equals判断是否俩对象是否相同
    • 如果俩个对象的hashCode不同等,那么就不再使用equals进行判断了,因为hashCode不同的俩个对象一定是不同的俩个对象!
  • 重写hashCodeequals后,HashSet就可以按照我们的要求进行去重了
  • TreeSet

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

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

(1)自然排序
如果一个类,实现了 java.lang.Comparable 接口,那么这个类的俩个对象就是可以比较大小的。

1.compareTo方法使用说明: int result = o1.compareTo(o2);
compareTo方法返回值代表的含义(三种情况:正数、负数、零)

  • result的值大于0,说明o1比o2大
  • result的值小于0,说明o1比o2小
  • result的值等于0,说明o1与o2相等

2.TreeSet中会把对象强制为Comparable类型,因为转成Comparable类型就可以调用compareTo方法进行俩个对象的比较大小了。

Integer类是实现了 Comparable 接口的,那么Integer类的俩个对象之间就可以调用 compareTo 方法比较大小了,当前对象比较出来大小,那么对象就可以按照从小到大的顺序进行排序。
Integer的俩个对象比较大小,其实就是比较Integer对象对应的int值的大小

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

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

	@Override 
	public int compareTo(Object o) { 
		//注意,这里this代表我,other代表你,也就是当前要和我比较大小的对象 
		Student other = (Student)o; 

		//如果我的年龄比你大,就返回正数,说明我比你大 
		if(this.age > other.age){ 
			return 1; 
		}
		//如果我的年龄比你小,就返回负数,说明我比你小 
		else if(this.age < other.age){ 
			return -1; 
		}
		//其他情况返回0,说明俩个对象(我和你)一样大 
		else {
			return 0; 
		} 
	} 
}

public static void main(String[] args) { 
	Set set = new TreeSet();
	set.add(new Student("mary",23)); 
	set.add(new Student("jack",21)); 
	set.add(new Student("tom",20)); 
	set.add(new Student("lucy",22));
	
	for(Object obj : set){ 
		System.out.println(obj); 
	} 
}

//运行结果: 
	Student{name='tom', age=20} 
	Student{name='jack', age=21} 
	Student{name='lucy', age=22} 
	Student{name='mary', age=23}

(2)比较器排序
java.util.Comparator 接口,是一个比较器接口,它的实现类可以对俩个对象作出大小的比较,即使对象没有实现 Comparable 接口,也可以进行比较。
TreeSet类重载的构造器中,有一个构造器可以接收比较器对象

比较器的使用规则:int result = compare(o1, o2);

  • result的值大于0,说明o1比o2大
  • result的值小于0,说明o1比o2小
  • result的值等于0,说明o1与o2相等

这里和自然排序的规则是一样的,只关心正数、负数、零,不关心具体的值是多少

public class Student{ 
	String name; 
	int age; 

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

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

public static void main(String[] args) { 
	//使用Comparator接口,创建出匿名内部类对象,这个对象就是要用的比较器对象 
	Comparator c = new Comparator() { 
		@Override 
		public int compare(Object o1, Object o2) { 
			Student s1 = (Student) o1; 
			Student s2 = (Student) o2; 
			return s1.age > s2.age? 1 : (s1.age==s2.age? 0 : -1); 
		} 
	};

	//创建TreeSet对象的时候,把比较器对象传入 
	Set set = new TreeSet(c); 
	set.add(new Student("mary",23)); 
	set.add(new Student("jack",21)); 
	set.add(new Student("tom",20)); 
	set.add(new Student("lucy",22));

	for(Object obj : set){ 
		System.out.println(obj); 
	} 
}

//运行结果: 
	Student{name='tom', age=20} 
	Student{name='jack', age=21} 
	Student{name='lucy', age=22} 
	Student{name='mary', age=23}

10.Map集合

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

Map类型集合与Collection类型集合,存储数据的形式不同:

  • Collection类型集合中,每次存一个数据。
  • Map类型集合中,每次需要存一对数据,key-value(键值对)
    • key值必须是唯一的,value值允许重复
    • 键(key)和值(value)一一映射,一个key对应一个value
    • 在Map中,通过key值(唯一的),可以快速的找到对应的value值

Map接口中的方法:

//把key-value存到当前Map集合中 
V put(K key, V value) 

//把指定map中的所有key-value,存到当前Map集合中 
void putAll(Map<? extends K,? extends V> m) 

//当前Map集合中是否包含指定的key值 
boolean containsKey(Object key) 

//当前Map集合中是否包含指定的value值 
boolean containsValue(Object value) 

//清空当前Map集合中的所有数据 
void clear() 

//在当前Map集合中,通过指定的key值,获取对应的value 
V get(Object key) 

//在当前Map集合中,移除指定key及其对应的value 
V remove(Object key) 

//返回当前Map集合中的元素个数(一对key-value,算一个元素数据) 
int size() 

//判断当前Map集合是否为空
boolean isEmpty() 

//返回Map集合中所有的key值 
Set<K> keySet() 

//返回Map集合中所有的value值 
Collection<V> values() 

//把Map集合中的的key-value封装成Entry类型对象,再存放到set集合中,并返回 
Set<Map.Entry<K,V>> entrySet()

11.Map实现类

Map 接口有很多实现类,以及子接口:

  • HashMap (重要,最常用)
  • HashTable (线程安全)
  • TreeMap (支持key排序)
  • LinkedHashMap(存入顺序就是取出顺序)
  • HashMap :存储数据采用的哈希表结构,元素的存取顺序不能保证一致。由于要保证键的唯一、不重复,需要重写键的hashCode()方法、equals()方法。
  • HashTable :和之前List集合中的 Vector 的功能类似,可以在多线程环境中,保证集合中的数据的操作安全,类中的方法大多数使用了 synchronized 修饰符进行加锁。
  • TreeMap :该类是 Map 接口的子接口 SortedMap 下面的实现类,和 TreeSet 类似,它可以对key值进行排序,同时构造器也可以接收一个比较器对象作为参数。支持key值的自然排序和比较器排序俩种方式。

(1)自然排序:

public static void main(String[] args) { 
	Map map = new TreeMap(); 
	map.put(4,"mary"); 
	map.put(2,"jack"); 
	map.put(1,"tom"); 
	map.put(3,"lucy"); 
	
	Set keys = map.keySet();
	for(Object key : keys){ 
		System.out.println(key+" : "+map.get(key));
	} 
}

//运行结果: 
	1 : tom 
	2 : jack 
	3 : lucy 
	4 : mary

(2)比较器排序:

public static void main(String[] args) { 
	Comparator c = new Comparator() { 
	@Override 
		public int compare(Object o1, Object o2) { 
			Integer k1 = (Integer) o1; 
			Integer k2 = (Integer) o2; 
			//注意,这里在默认的比较结果基础上加了一个符号,即原来返回的正数变负数、返回的负数 变正数 
			return -k1.compareTo(k2); 
		} 
	};
	
	//构造器中传入比较器对象 
	Map map = new TreeMap(c); 
	map.put(4,"mary"); 
	map.put(2,"jack"); 
	map.put(1,"tom");
	map.put(3,"lucy"); 
	
	Set keys = map.keySet();
	for(Object key : keys){ 
		System.out.println(key+" : "+map.get(key)); 
	} 
}

//运行结果: 
	4 : mary 
	3 : lucy 
	2 : jack 
	1 : tom
  • LinkedHashMap :该类是 HashMap 的子类,存储数据采用的哈希表结构+链表结构。通过链表结构可以保证元素的存取顺序一致;
public static void main(String[] args) { 
	Map map = new LinkedHashMap();
	map.put(4,"mary"); 
	map.put(2,"jack"); 
	map.put(1,"tom"); 
	map.put(3,"lucy"); 

	Set keys = map.keySet();
	for(Object key : keys){ 
		System.out.println(key+" : "+map.get(key)); 
	} 
}

//运行结果: 
	4 : mary 
	2 : jack 
	1 : tom 
	3 : lucy

12.Map的遍历

public static void main(String[] args) { 	
	Map map = new LinkedHashMap();
	map.put(1,"tom"); 
	map.put(2,"jack"); 
	map.put(3,"lucy"); 
	map.put(4,"mary");

	//Map集合的三种遍历方式,
}

(1)Map中的keySet方法,可以返回Map集合中所有的key值的集合:

Set keys = map.keySet();
for(Object key : keys){ 
	System.out.println(key+" : "+map.get(key)); 
}

(2)Map中的values方法,可以返回Map集合中所有的value值的集合

Collection values = map.values();
for(Object value : values){ 
	System.out.println(value); 
}

(3)Map 接口中有定义了一个内部接口 Entry

  • 一个Entry类型的对象,可以代表Map集合中的一组key-value(键值对),并且提供了获取key值和value值的方法。
  • Map 接口中的 entrySet() 方法,就是将 Map 集合中的每一组key-value(键值对)都封装成一个Entry 类型对象,并且把这些个 Entry 对象存放到Set集合中,并返回。
Set entrySet = map.entrySet();
for(Object obj : entrySet){ 
	Map.Entry entry = (Map.Entry) obj; 
	System.out.println(entry.getKey()+" : "+entry.getValue()); 
}

13. 集合工具类

java.util.Arrays 是一个工具类,专门用来操作数组对象的,里面都是静态方法,可直接调用。
java.util.Collections 也是一个工具类,专门用来操作集合对象的,里面都是静态方法,可以直接调用。

Collections 中的常用方法:

  • fill方法,使用指定元素替换指定列表中的所有元素
List list = new ArrayList(); 
list.add(1); 
list.add(2); 
list.add(3); 
Collections.fill(list, 20);
for(Object o:list){ 
	System.out.println(o); 
}
  • max方法,根据元素的自然顺序,返回给定集合的最大元素
List list = new ArrayList(); 
list.add(1); 
list.add(9); 
list.add(3); 
System.out.println(Collections.max(list));
  • min方法,根据元素的自然顺序,返回给定集合的最小元素
  • reverse方法,反转集合中的元素
List list = new ArrayList(); 
list.add(1); 
list.add(9); 
list.add(3); 
Collections.reverse(list);
for(Object o:list){ 
	System.out.println(o); 
}
  • sort方法,根据元素的自然顺序,对指定列表按升序进行排序
List list = new ArrayList(); 
list.add(1); 
list.add(9); 
list.add(3); 
//如果需要,也可以在第二个参数位置传一个比较器对象 
//Collections.sort(list,c); 
Collections.sort(list);
for(Object o:list){ 
	System.out.println(o); 
}
  • shuffle方法,使用默认随机源对指定列表进行置换
List list = new ArrayList(); 
list.add(1); 
list.add(9); 
list.add(3); 
Collections.shuffle(list);
for(Object o:list){ 
	System.out.println(o); 
}
  • addAll方法,往集合中添加一些元素
List list = new ArrayList(); 
//注意,addAll的第二个参数,是可变参数 
Collections.addAll(list,1,3,5,7);
for(Object o:list){ 
	System.out.println(o); 
}
  • synchronizedCollection,把非线程安全的Collection类型集合,转为线程安全的集合
  • synchronizedList,把非线程安全的List类型集合,转为线程安全的集合
  • synchronizedSet,把非线程安全的Set类型集合,转为线程安全的集合
  • synchronizedMap,把非线程安全的Map类型集合,转为线程安全的集合

二、泛型

1.概述

  • T是泛型参数,表示一种数据类型,具体是什么类型,需要将来使用Point的时候进行传参来确定
  • 如果将来Point在使用的时候,没有给泛型参数T传值,那么T默认就表示为Object类型
  • T是泛型参数的名字,也就是相当于形参,名字随便起,但是一般用一个有意义的大写字母
public class Point<T>{ 
	T x; 
	T y; 
}

Point<T> 类的使用:

  • 给泛型参照传的值,只能是引用类型,不能是基本类型: Point<int> 编译报错
public static void main(String[] args) { 
	//p1对象中x和y的属性类型都是Integer 
	Point<Integer> p1 = new Point<Integer>(); 
	p1.x = 1; p1.y = 2; 

	//p2对象中x和y的属性类型都是
	String Point<String> p2 = new Point<String>(); 
	p2.x = "1"; 
	p2.y = "2"; 

	//p3对象中x和y的属性类型都是Object 
	Point p3 = new Point(); 
	p3.x = new Object(); 
	p3.y = new Object(); 
}

2. 集合的泛型

(1)Collection接口

  • Collection是一个泛型接口,泛型参数是E,在接口中,add方法的参数类型也使用了E。
  • 在使用Collection接口的时候,如果没有给泛型参数传值,那么这个E就默认表示为Object, add方法就可以接收任意类型的对象。
  • 集合中如果存储的数据类型不一致,就会在强制转换的时候出现类型转换异常
  • 如果在使用Collection接口的时候,给泛型参数指定了具体类型,那么就会防止出现类型转换异常的情况,因为这时候集合中添加的数据已经有了一个规定的类型,其他类型是添加不进来的。
public static void main(String[] args) { 
	Collection<String> c = new ArrayList<String>(); 
	//可以简写成:Collection<String> c = new ArrayList<>();
	c.add("hello1"); 
	c.add("hello2"); 
	c.add("hello3"); 
	//编译报错,add(E e) 已经变为 add(String e) 
	//int类型的数据1,是添加不到集合中去的 
	//c.add(1);

	for(String str : c){ 
		System.out.println(str); 
	} 
}

(2)Map 接口使用泛型

public static void main(String[] args) { 
	Map<Integer,String> map = new HashMap<>(); 
	//根据泛型类型的指定,put方法中的key只能是Integer类型,value只能是String类型 
	map.put(1,"hello1"); 
	map.put(2,"hello2"); 
	map.put(3,"hello3"); 
	map.put(4,"hello4"); 
	
	//根据上面列出的源码可知,当前指定Map的泛型类型为:Map<Integer,String> map 
	//entrySet方法返回的类型就应该是Set<Map.Entry<Integer, String>> 
	Set<Map.Entry<Integer, String>> entrySet = map.entrySet();

	for(Map.Entry entry:entrySet){ 
		System.out.println(entry.getKey()+" : "+entry.getValue()); 
	} 
}

3.泛型的种类

java中的泛型分三种使用情况:

  • 泛型类
  • 泛型接口
  • 泛型方法

(1)泛型类

  • 如果泛型参数定义在类上面,那么这个类就是一个泛型类
  • 在类中,就可以使用这个T来代表某一个类型,这个类型具体是什么将来使用的时候再传参确定
public class Point<T>{...} 

public static void main(String[] args){ 
	Point<String> p = new Point<>(); 
}

(2)泛型接口

  • 如果泛型参数定义在接口上面,那么这个接口就是一个泛型接口
  • 在接口中,就可以使用这个T来代表某一个类型,这个类型具体是什么将来使用的时候再传参确定
public interface Action<T>{...} 

public static void main(String[] args){ 
	//创建匿名内部类 
	Action<String> a = new Action<>(){ //... }; 
}

(3)泛型方法
如果泛型参数定义在方法上面,那么这个方法就是一个泛型方法

public class Test{ 
	public <T> T test(T t){ //.. } 
}

public static void main(String[] args){ 
	Test t = new Test(); 
	String str = t.test("hello"); 
	Integer i = t.test(1); 
	Double d = t.test(10.5D); 
}
  • =号俩边的所指定的泛型类型,必须是要一样的,这里说的泛型类型,指的是<>中所指定的类型
  • 虽然 IntegerObject 的子类型,但是 ArrayList<Integer>ArrayList<Object> 之间没有子父类型的关系,它们就是俩个不同的类型

4.通配符

(1)可以使用通配符**(?)**来表示泛型的父类型:
问号就是通配符,可以匹配所有的泛型类型

public void test(Collection<?> c){
	 //test方法可以接收泛型是任意类型的Collection集合对象
}

(2)虽然使用通配符(?)的集合,不能再往其中添加数据了,但是有一个值是可以添加到集合中的null,可以遍历集合取出数据:

public static void main(String[] args) { 
	ArrayList<String> list = new ArrayList<>(); 
	list.add("hello1"); 
	list.add("hello2"); 
	list.add("hello3"); 
	list.add("hello4"); 
	Collection<?> c = list; 

	//编译报错 
	//c.add("hello5");

	for(Object obj : c){ 
		System.out.println(obj); 
	} 
}

5.泛型边界

在默认情况下,泛型的类型是可以任意设置的,只要是引用类型就可以。
如果在泛型中使用 extendssuper关键字,就可以对泛型的类型进行限制。即:规定泛型的上限和下限。

(1)泛型中 extendssuper 对比:

  • 使用extends可以定义泛型的【上限】,这个就表示将来泛型所接收的类型【最大】是什么类型。可以是这个最大类型或者它的【子类型】。
    • 例如: List<? extends Number> list
    • 将来引用list就可以接收泛型是 Number 或者 Number 子类型的List集合对象
  • 使用super可以定义泛型的【下限】,这个就表示将来泛型所接收的类型【最小】是什么类型。可以是这个【最小类型】或者它的【父类型】。
    • 例如: List<? super Number> list
    • 将来引用list就可以接收泛型是 Number 或者 Number 父类型的List集合对象

(2)extends 限定泛型的上限的使用场景

  1. 在声明泛型类或者泛型接口的时候【可以】使用
public class Point<T extends Number>{ 
	private T x; 
	private T y; 
}

interface Action<T extends Person>{ 
	public void test(T t); 
}
  1. 在声明泛型方法的时候【可以】使用
public <T extends Action> void test(T t);
  1. 在声明变量的时候【可以】使用
public class Test{ 
	public void test(List<? extends Number> list){ } 
}

public static void main(String[] args) { 
	List<? extends Number> list = new ArrayList<Long>(); 
	t.test(list); 
}

(3)super 限定泛型的下限的使用场景

  1. 在声明泛型类或者泛型接口的时候【不能】使用
  2. 在声明泛型方法的时候【不能】使用
  3. 在声明变量的时候【可以】使用
public class Test{ 
	public void test(List<? super Number> list){ } 
}

public static void main(String[] args) { 
	List<? super Number> list; 
	list = new ArrayList<Number>(); 
	list = new ArrayList<Object>(); 
	
	//假设Student 继承了 Person 
	List<? super Student> list; 
	list = new ArrayList<Student>(); 
	list = new ArrayList<Pesson>(); 
	list = new ArrayList<Object>(); 
}

6.类型擦除、泛型擦除

(1)泛型类型仅存在于编译期间,编译后的字节码和运行时不包含泛型信息,所有的泛型类型映射到同一份字节码。
(2)Java的泛型只存在于编译时期,泛型使编译器可以在编译期间对类型进行检查以提高类型安全,减少运行时由于对象类型不匹配引发的异常。
(3)在编译成功后,所有泛型信息会被擦除,变为原始类型Object

三、 枚举

1.概述

通过关键字 enum 来定义枚举类。
枚举类是一种特殊的类,它和普通类一样可以使用构造器、定义成员变量和方法,也能实现一个或多个接口。
枚举类不能继承其他类

  • 枚举其实也是一种类,同时还是一个final修饰的类
  • 枚举类型都会默认继承一个父类型: java.lang.Enum ,这还是一个抽象的泛型类
  • 枚举中所定义的对象,其实就是类里面的public static final修饰的常量,并且这些常量会在静态代码块中做初始化
  • 枚举类型中还一个默认的私有构造器,说明我们在外面并不能自己去创建枚举类型的对象
  • 枚举类型中还有默认添加进来的方法
    • values() 方法,可以返回这个枚举类型的所有对象,返回类型是数组
    • valueOf(String str) 方法,通过一个字符串可以返回枚举对象,这个字符串参数就是枚举对象的名字
  • 枚举类型会从父类中继承过来一些方法(具体可以查看其固定的父类型),例如
    • String name(),返回这个枚举对象的名字
    • int ordinal(),返回这个枚举对象的编号,默认从0开始

2.获取枚举对象

在枚举类型中,提前将这个类型的对象个数和对象名字都固定下来,并且之后的使用中不会改变,也不会再创建其他对象。

public enum Gender{ 
	MALE,FEMALE 
}
  • 方式一:
public static void main(String[] args){ 
	//使用类名直接访问类中定义的俩对象 
	//最常用的一种方式 
	Gender g = Gender.MALE; 
	g = Gender.FEMALE; 

	//可以调用从父类型Enum以及Object中继承过来的方法 
	System.out.println(g.name()); 
	System.out.println(g.ordinal()); 
	System.out.println(g.toString()); 
}

//运行结果: 
		MALE 
		0
		MALE
  • 方式二:
public static void main(String[] args){ 
	//通过字符串参数的改变,可以获取到Gender中的指定的一个对象 
	String name = "MALE"; 
	Gender g = Gender.valueOf(name); 
	System.out.println(g.name()); 
	System.out.println(g.ordinal()); 
	System.out.println(g.toString()); 
}

//运行结果: 
		MALE 
		0
		MALE
  • 方式三:
public static void main(String[] args){ 
	//通过字符串确定是哪一个枚举类型 
	Class c = Class.forName("com.briup.demo.Genger"); 
	
	//通过字符串确定是哪一个名字的枚举对象 
	String name = "FEMALE"; 
	
	//可以通过改变字符串,获取到java中任何一个枚举类型中的任意一个枚举对象 
	Enum g = Enum.valueOf(c,name); 
	System.out.println(g.name()); 
	System.out.println(g.ordinal()); 
	System.out.println(g.toString()); 
}

//运行结果: 
		FEMALE 
		1
		FEMALE

3.枚举中定义属性和方法

  • 在枚举类型中,除了可以指定对象的个数和名称之外,还可以定义属性和方法
  • 枚举类型中的第一行代码,要求一定是指定枚举对象的个数和名字,同时最后面加分号(;),在这行代码下, 才可以定义枚举类型的属性和方法
public enum Gender{
	 MALE,FEMALE; 
	
	 private String name; 
	 
	 public void setName(String name){ 
	 	this.name = name; 
	 }
	
	 public String getName(){ 
	 	return this.name; 
	 }
	 
	 public void test(){ 
	 	System.out.println("Gender test..."); 
	 }
	 
	 public static void print(String name){ 
	 	System.out.println("hello "+name); 
	 } 
}

public static void main(String[] args){ 
	Gender g = Gender.MALE; 
	g.test(); 
	g.setName("我是男生"); 
	System.out.println(g.getName()); 
	Gender.print("jack");
}

4.枚举中定义构造器

枚举中的构造器,只能使用private修饰,或者不写修饰符,那么默认也是private,同时还可以构造器重载

  • 调用无参构造器
    在枚举中,定义对象的时候,就已经默认使用了无参构造器
public enum Gender{ 
	MALE(),FEMALE(); 
	
	//构造器,默认private修饰 
	Gender(){ 
		System.out.println("无参构造器被调用"); 
	} 
}

public static void main(String[] args)throws Exception { 
	//该代码,可以对Gender.class进行类加载 
	Class.forName("com.briup.demo.Gender"); 
}

//运行结果: 
		无参构造器被调用 
		无参构造器被调用
  • 调用有参构造器
public enum Gender{ 
	MALE("男"),FEMALE("女"); 
	private String name; 

	Gender(){ 
		System.out.println("无参构造器被调用"); 
	}
	
	Gender(String name){ 
		this.name = name; 
		System.out.println("有参构造器被调用"); 
	} 
}

public static void main(String[] args)throws Exception { 
	//该代码,可以对Gender.class进行类加载 
	Class.forName("com.briup.demo.Gender"); 
}

//运行结果: 
		有参构造器被调用 
		有参构造器被调用

5.枚举中定义抽象方法

需要在定义每一个枚举对象的时候,将抽象方法进行实现,因为枚举类型是final修饰的类,不可能有子类型来实现这个抽象方法,所以就必须是自己的对象去实现这个抽象方法

public enum Gender{ 
	MALE(){ 
		@Override 
		public void test() { 
			System.out.println("男生测试"); 
		} 
	},FEMALE(){ 
		@Override 
		public void test() { 
			System.out.println("女生测试"); 
		} 
	};
	public abstract void test(); 
}

6.枚举类型实现接口

枚举类型已经有默认的父类型( java.lang.Enum ),我们就不能让它在继承其他父类了,但是我们可以让枚举类型实现指定的接口。
需要在枚举类型中,实现接口中的抽象方法,这里有俩种方式:

  • 方式一:在枚举中,统一实现这个接口中的抽象方法
public enum Gender implements Action{ 
	MALE,FEMALE; 
	@Override 
	public void run() { 
		System.out.println("枚举中统一实现接口中的抽象方法"); 
	} 
}

interface Action{ 
	void run(); 
}
  • 方式二:在每个枚举对象中,分别实现这个接口中的抽象方法
public enum Gender implements Action{ 
	MALE(){ 
		@Override 
		public void run() {
			System.out.println("男生对象中,单独实现接口中的抽象方法"); 
		} 
	},FEMALE(){ 
		@Override 
		public void run() { 
			System.out.println("女生对象中,单独实现接口中的抽象方法"); 
		} 
	}; 
}

interface Action{ 
	void run();
}

四、反射

  • 反射是java中提供的一种机制,它允许我们在程序运行的时候,动态获取一个类中的基本信息,并且可以调用类中的属性、方法、构造器。
  • 在代码运行的时候,也可以通过反射机制获取到这个类中的属性、方法、构造器,以及调用它们。

1.Class类型

java.lang.Class 是API中提供的一个类,它可以表示java中所有的类型,包括基本类型和引用类型。
obj.getClass(); 这句代码的含义就是:返回obj引用在运行时所指向对象的实际类型。

(1)使用Class类的对象来表示基本类型

public static void main(String[] args)throws Exception { 
	//这个对象c就代表java中的int类型 
	Class c = int.class; 
	
	//判断对象c所代表的类型是否是基本类型 
	System.out.println(c.isPrimitive()); 
	
	//获取对象c所代表的类型的名字 
	System.out.println(c.getName()); 
}

//运行结果: 
		true 
		int

(2)使用Class类的对象来表示类类型

public static void main(String[] args)throws Exception { 
	//这个对象c就代表Student类 
	Class c = Student.class; 
	System.out.println(c.getName()); 
}

//运行结果: 
		com.briup.demo.Student

(3)使用Class类的对象来表示接口类型

public static void main(String[] args)throws Exception { 
	//这个对象c就代表List接口类型 
	Class c = List.class; 
	System.out.println(c.isInterface()); 
	System.out.println(c.getName()); 
}

//运行结果: 
		true 
		java.util.List

(4)使用Class类的对象来表示数组类型

public static void main(String[] args)throws Exception { 
	//这个对象c代表数组类型 
	Class c; 
	c = int[].class;
	c = int[][].class; 
	c = Student[].class; 
	
	System.out.println(c.isArray()); 
	System.out.println(c.getSimpleName()); 
	
	//返回组成该数组具体类型是什么 
	System.out.println(c.getComponentType().getSimpleName()); 
}

//运行结果: 
		true 
		Student[] 
		Student

2.获取Class对象

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

(1)获取基本类型的Class对象:

  • 只有一种方式:
    Class c = int.class;

(2)获取接口类型的Class对象:

  • 有俩种方式:
    • Class c1 = Action.class;
    • Class c2 = Class.forName("com.briup.demo.Action");

(3)获取数组类型的Class对象:

  • 有俩种方式:
    • Class c1 = int[].class;
    • int[] arr = new int[4]; Class c2 = arr.getClass();

(3)获取类类型的Class对象:

  • 有三种方式:
    • Class c1 = Student.class; //使用类型名称直接获取
    • Class c2 = Class.forName("com.briup.demo.Student"); //使用Class类中的静态方法forName获取
    • Student stu = new Student(); Class c3 = stu.getClass(); //使用对象调用getClass方法获取

3.获取类的信息

public class Student implements Action,Mark{ 
	private String name; 
	int age; 
	public static int num; 
	
	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 String sayHello(String name){ 
		return "hello! "+name; 
	}
	@Override 
	
	public void run() { }
	@Override 
	public void star() { } 
}

interface Action{ 
	void run(); 
}

interface Mark{ 
	void star(); 
}

(1)获取类的基本信息

public static void main(String[] args)throws Exception { 
	Student stu = new Student(); 
	Class c = stu.getClass(); 
	
	//获取类的名字,全限定名 
	System.out.println(c.getName()); 
	
	//获取类的名字,简单类名 
	System.out.println(c.getSimpleName()); 
	
	//获取类所属包的名字 
	System.out.println(c.getPackage().getName()); 
	
	//获取类的修饰符 
	System.out.println(Modifier.toString(c.getModifiers())); 
	
	//获取类的父类型的名字 
	System.out.println(c.getSuperclass().getName()); 
	
	//获取类实现的所有接口 
	System.out.println(Arrays.toString(c.getInterfaces())); 
	
	Class c2 = Object.class; 
	Class c3 = Action.class; 
	Class c4 = Mark.class; 
	Class c5 = String.class; 
	
	//判断c2代表的类型是不是c代表类型的父类型 
	System.out.println(c2.isAssignableFrom(c)); 
	
	//判断c3代表的类型是不是c代表类型 的父类型 
	System.out.println(c3.isAssignableFrom(c)); 
	
	//判断c4代表的类型是不是c代表类型 的父类型 
	System.out.println(c4.isAssignableFrom(c)); 
	
	//判断c5代表的类型是不是c代表类型 的父类型 
	System.out.println(c5.isAssignableFrom(c)); 
}

//运行结果: 
		com.briup.demo.Student S
		tudent com.briup.demo 
		public java.lang.Object 
		[interface com.briup.demo.Action, interface com.briup.demo.Mark] 
		true 
		true 
		true
		false

(2)获取类中声明的属性
java.lang.reflect.Field 表示类中的属性

  • 获取类中的public修饰的属性,也包含从父类中继承过来的public属性
    • Field[] getFields()
    • Field getField(String name)
  • 获取类中声明的属性(包含私有的),但是不能获取从父类中继承过来的属性
    • Field[] getDeclaredFields()
    • Field getDeclaredField(String name)
public static void main(String[] args)throws Exception { 
	Class c = Class.forName("com.briup.demo.Student");
	Field[] fields = c.getDeclaredFields();
	for(Field f : fields){ 
		System.out.println("属性的修饰符:"+Modifier.toString(f.getModifiers())); 
		System.out.println("属性的类型:"+f.getType().getName()); 
		System.out.println("属性的名称:"+f.getName()); 
		System.out.println("---------------------"); 
	} 
}

//运行结果: 
		属性的修饰符:private 
		属性的类型:java.lang.String 
		属性的名称:name
		---------------------
		属性的修饰符: 
		属性的类型:int 
		属性的名称:age
		---------------------
		属性的修饰符:public static 
		属性的类型:int 
		属性的名称:count

(3)获取类中声明的方法
java.lang.reflect.Method 表示类中的方法

  • 获取当前类中的public方法,包含从父类中继承的public方法
    • Method[] getMethods()
    • Method getMethod(String name, Class<?>... parameterTypes)
  • 获取当前类中声明的方法(包含私有的),但是不能获取从父类中继承过来的方法
    • Method[] getDeclaredMethods()
    • Method getDeclaredMethod(String name, Class<?>... parameterTypes)
public static void main(String[] args)throws Exception { 
	Student stu = new Student(); 
	Class c = stu.getClass(); 
	
	//获取类中声明的所有方法 
	Method[] methods = c.getDeclaredMethods();
	
	for(Method m : methods){ 
		//获取方法的修饰符 
		System.out.println(Modifier.toString(m.getModifiers())); 
		
		//获取方法的返回类型 
		System.out.println(m.getReturnType().getSimpleName()); 
		
		//获取方法的名字 
		System.out.println(m.getName());
		System.out.println("方法参数个数:"+m.getParameterCount()); 
		
		//获取方法的参数列表 
		Class[] paramArr = m.getParameterTypes(); 
		
		//输出方法的参数列表 
		System.out.println("\t"+Arrays.toString(paramArr)); 
		
		//获取方法所抛出的异常类型 
		Class[] exceptionArr = m.getExceptionTypes(); 
		System.out.println("方法抛出异常个数:"+exceptionArr.length); 
		
		//输出方法所抛出的异常列表 
		System.out.println("\t"+Arrays.toString(exceptionArr)); 
		System.out.println("-------------------------------"); 
	} 
}

//运行结果: 
		public 
		void 
		run 
		方法参数个数:0 
		[] 
		方法抛出异常个数:0 
		[]
-------------------------------
		public 
		String 
		getName 
		方法参数个数:0 
		[] 
		方法抛出异常个数:0 
		[]
-------------------------------
		public 
		void 
		setName 
		方法参数个数:1 
		[class java.lang.String] 
		方法抛出异常个数:0 
		[]
-------------------------------
		public 
		String 
		sayHello 
		方法参数个数:1 
		[class java.lang.String] 
		方法抛出异常个数:0 
		[]
-------------------------------
		public 
		void 
		star 
		方法参数个数:0 
		[] 
		方法抛出异常个数:0 
		[]
-------------------------------

(4)获取类中声明的构造器
java.lang.reflect.Constructor 表示类中的方法

  • 获取当前类中的public构造器
    • public Constructor<?>[] getConstructors()
    • public Constructor<T> getConstructor(Class<?>... parameterTypes)
  • 获取当前类中的所有构造器,包含私有的
    • public Constructor<?>[] getDeclaredConstructors()
    • public Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes)
public static void main(String[] args)throws Exception { 
	Student stu = new Student(); 
	Class c = stu.getClass(); 
	
	//获取类中所有的public构造器 
	Constructor[] constructors = c.getConstructors();
	
	for(Constructor constructor:constructors){ 
		//构造器的修饰符 
		System.out.println(Modifier.toString(constructor.getModifiers())); 
		
		//构造器的名字 
		System.out.println(constructor.getName()); 
		
		//构造器的参数列表 
		Class[] paramList = constructor.getParameterTypes(); 	
		System.out.println(java.util.Arrays.toString(paramList)); 
		
		//构造器的抛出异常 
		Class[] exceptionList = constructor.getExceptionTypes(); 	
		System.out.println(java.util.Arrays.toString(exceptionList)); 
		System.out.println("-----------------------------"); 
	} 
}

//运行结果: 
		public 
		com.briup.demo.Student 
		[]
		[]
		-----------------------------
		public 
		com.briup.demo.Student 
		[class java.lang.String, int] 
		[]
-----------------------------

4.反射访问属性

public static void main(String[] args)throws Exception { 
	Student stu = new Student(); 
	Class c = stu.getClass(); 
	
	//获取类中名字叫name的属性 
	Field f1 = c.getDeclaredField("name"); 
	
	//设置私有属性可以被访问,否则报错
	f1.setAccessible(true); 
	
	//用反射的方式,给指定对象的name属性赋值 
	//相当于之前的stu.name = "tom";
	f1.set(stu,"tom"); 
	
	//用反射的方式,获取这个属性的值 
	//相当于之前的stu.name 
	System.out.println(f1.get(stu)); 
	System.out.println("----------------------"); 
	
	//获取类中名字叫age的属性 
	Field f2 = c.getDeclaredField("age"); 
	
	//用反射的方式,给指定对象的age属性赋值 
	//相当于之前的stu.age = 20;
	f2.set(stu,20); 
	
	//用反射的方式,获取这个属性的值 
	//相当于之前的stu.age 
	System.out.println(f2.get(stu)); 
	System.out.println("----------------------"); 
	
	//获取类中名字叫num的属性 
	Field f3 = c.getDeclaredField("num"); 
	
	//用反射的方式,给静态属性num赋值,不需要对象 
	//相当于之前的Student.num = 99;
	f3.set(null,99); 
	
	//用反射的方式,获取这个静态属性的值,不需要对象 
	//相当于之前的Student.num 
	System.out.println(f3.get(null)); 
}

5.反射调用方法

public static void main(String[] args)throws Exception { 
	Student stu = new Student(); 
	Class c = stu.getClass(); 
	
	//获取类中的toString方法,没有参数,这是从父类继承的方法 
	Method m1 = c.getMethod("toString", null); 
	
	//反射的方式,调用stu对象中的这个方法,没有参数,并接收执行结果 
	//相当于之前的:Object result = stu.toString(); 
	Object result = m1.invoke(stu,null);
	
	//输出执行结果 
	System.out.println(result); 
	System.out.println("-------------------"); 
	
	//获取类中的sayHello方法,需要一个String类型的参数,这是自己定义的方法 
	Method m2 = c.getMethod("sayHello", String.class); 
	
	//反射的方式,调用stu对象中的这个方法,参数是"tom",并接收执行结果 
	//相当于之前的:Object result = stu.sayHello("tom"); 
	result = m2.invoke(stu,"tom"); 
	
	//输出执行结果 
	System.out.println(result); 
}

6.反射创建对象

使用反射的方式可以创建对象,也就是需要反射调用构造器。
(1)反射调用类中无参构造器创建对象

public static void main(String[] args)throws Exception { 
	Class c = Student.class; 
	
	//默认调用类中的无参构造器来创建对象 
	//相当于之前的:Object obj = new Student(); 
	Object obj = c.newInstance(); 
	System.out.println(obj); 
}

(2)反射调用类中有参构造器创建对象

public static void main(String[] args)throws Exception { 
	Class c = Student.class; 
	//获取类中的俩参构造器 
	Constructor constructor = c.getConstructor(String.class, int.class); 
	
	//调用有参构造器创建对象,并传入对应的参数值 
	//相当于之前的:Object obj = new Student("tom",20); 
	Object obj = constructor.newInstance("tom",20); 
	System.out.println(obj);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值