黑马程序员——Java基础---泛型和反射

时间:2021-10-12 00:48:01
------Java培训、Android培训、iOS培训、.Net培训、期待与您交流! -------


泛型和反射

一、泛型

        一、泛型概述

        泛型是JDK1.5版本以后出现的新特性,它主要是在编译时对类型进行检查,是一个类型安全机制,用于解决安全问题。

        好处:1、让运行时异常ClassCastException,转移到了编译时期。方便程序员解决问题,让运行时问题减少,安全。

                   2、避免了强制转换麻烦。

        泛型的格式为:通过<>来定义要操作的引用数据类型。

        在使用java格式提供的对象时,什么时候使用泛型呢?通常在集合框架很常见,只要<>就是用来接收类型的,当使用集合时,将集合中要存储的数据类型作为参数传递到<>中即可。

package com.itheima;
import java.util.*;
public class GenericDemo
{
public static void main(String[] args)
{
ArrayList<String> al=new ArrayList<String>();
al.add("张三");
al.add("李四");
al.add("王五");
Iterator<String> it = al.iterator();
while(it.hasNext())
{
System.out.println(it.next());
}
}
}

        二、泛型类

        在javaapi中有大量的用泛型定义的类,这是因为类中大量的参数和成员变量的引用数据类型是不确定的,这个时候需要在类名之后加上泛型,然后在使用时人为的指定该类中成员变量和参数类型到底是什么类型的,这样极大地增强了安全性,不需要再去强制转换。我们也可以自定义一个泛型类:
package com.itheima;

class Person
{

}
class Student
{

}
/*
class PersonTool
{
Object obj;

public PersonTool(Object obj) {
super();
this.obj = obj;
}
public Object getobj()
{
return obj;
}
}
*/
class PersonTool<T>
{
T t;

public PersonTool(T t) {
super();
this.t = t;
}
public T getT()
{
return t;
}
}
public class GenericDemo2
{
public static void main(String[] args)
{
Student s=new Student();
Person p1=new Person();
PersonTool<Student> p=new PersonTool<Student>(s);
PersonTool<Person> p2=new PersonTool<Person>(p1);
p.getT();
p2.getT();
}
}

        三、泛型方法

        当一个类中某一个方法的参数和变量的引用数据类型是不确定的,这是就需要在方法上加上泛型。之所以此时不在类上加泛型是因为泛型类明确泛型类型之后类中方法如果用到泛型则方法的类型也就明确了,可是某一个方法可能要操作的是String类型而另一个类型可能要操作Integer类型,此时明显使用泛型类是不合适的,这是就用泛型方法。

<span style="font-family: Arial, Helvetica, sans-serif;"></span><pre name="code" class="java">package udp;
class Demo
{
public<T> void show(T t)
{
System.out.println(t);
}
public <Q> void print(Q q)//泛型类的泛型定义在返回值类型之前
{
System.out.println(q);
}
        public static<W> show1(W w)
        {
                System.out.println(w);
        }}public class GenericDemo3 {public static void main(String[] args){Demo d=new Demo();d.show("haha");//明确了泛型为Stringd.print(4);//明确了泛型为Integer
                Demo.show1(true)//明确泛型为Boolean}}<span style="font-family: Arial, Helvetica, sans-serif;"> </span>
        注意:泛型类和泛型方法都是是可以嵌套使用的。

        静态方法是不可使用定在类上的泛型的,这是因为泛型类是需要对象明确之后泛型才会明确,而静态方法先于对象存在,随着类的存在而存在,而类存在时该泛型还不明确。如果静态方法需要用到泛型,则需将泛型定义在方法上。

        四、泛型接口

        对于抽象的接口也可以定义泛型,在实现该接口的时候在接口名之后加上明确的泛型则复写的时候就不用对参数进行强制转换,可以直接传入指定泛型的参数类型。类似于Comparator接口就是这样。
package udp;
import java.util.*;
class MyCompare implements Comparator<String>
{
public int compare(String o1, String o2)
{
if (o1.length()>o2.length())
return -1;
if (o1.length()<o2.length())
return 1;
else
return o1.compareTo(o2);
}
}

        五、泛型限定

        使用泛型时不能使用类型特有方法!泛型限定主要是使用<?> <? extends T> <? super T>它们分别代表任意类型通配符,该类是T以及T的子类,该类是T以及T的父类。

/*

class Student implements Comparable<Person>//<? super E>
{
public int compareTo(Person s)
{
this.getName()
}
}
*/
class Comp implements Comparator<Person>
{
public int compare(Person s1,Person s2)
{

//Person s1 = new Student("abc1");
return s1.getName().compareTo(s2.getName());
}
}

TreeSet<Student> ts = new TreeSet<Student>(new Comp());
ts.add(new Student("abc1"));
ts.add(new Student("abc2"));
ts.add(new Student("abc3"));

二、反射

        一、Class类

        大家都知道,在查阅JavaApi的过程中我们发现Java已经帮我们定义了很多很多的类,这些类描述个各种不同的现实事物,有异常,有集合,有线程等等等,那么这么多的类也是不是一个现实存在的事物呢?对的,这些类也是现实存在的事物,我们抽取它们的共性:类名,类的成员变量,类的成员函数,类所处的包等来构成一个描述Java类的类,这个类就叫做Class类。

        那么根据面向对象的特点,有类就有对象,那么Class类的对象是什么呢?从上面的描述可以看出,Class类的对象就是一个一个具体的定义好的类,不管这些类是不是JavaApi的类还是我们自定义的类,说白了,我们往往在运行一个程序的时候首先需要编译,通过编译产生的类的字节码文件,这些就是Class类的对象。

        显然Class类创建对象是不能通过构造函数的,因为Class类是一类非常特殊的类,它是不能通过构造函数来实例化对象的,实例化Class类有三种方法:

        1、具体某一类的类名.class:Class c1=System.class;

        2、获取某一个类的对象,然后通过对象的getClass方法获取Class对象:Student s=new Student(); Class c1=s.getClass();注意该方法是Objcet类的方法。

        3、利用Class类的静态方法forName(String 类名)来获取Class对象,该方法若类名是无效的,会抛出异常:Class c1=Class.forName("Student");

        注意,同一个类在内存中只能存在一份字节码文件,意思就是说Class类的对象——某一个类的字节码文件只能存在一份。方法3字符串一定要是完整的包名+类名。

package udp;
import java.util.*;
public class ReflectDemo 
{
<span style="white-space:pre"></span>public static void main(String[] args) throws ClassNotFoundException
<span style="white-space:pre"></span>{
<span style="white-space:pre"></span>Date s=new Date();
<span style="white-space:pre"></span>Class c1=Date.class;
<span style="white-space:pre"></span>Class c2=s.getClass();
<span style="white-space:pre"></span>Class c3=Class.forName("java.util.Date");//若该字符串不是类名或者该类名不存在则会抛出异常,一定要是完整的包名+类名
<span style="white-space:pre"></span>System.out.println(c1==c2);
<span style="white-space:pre"></span>System.out.println(c2==c3);
<span style="white-space:pre"></span>System.out.println(c1==c3);
<span style="white-space:pre"></span>}
}
        java预定义了九种预定义的Class对象,表示八个基本类型和void。这些对象由Java虚拟机创建,与其表示的基本数据类型同名,即:boolean,byte,short,int,long,char,float,double。

       可以利用Class类中isPrimitive()方法判断该Class类对象是否是基本数据类型字节码。

<pre name="code" class="java">package udp;
import java.util.*;
public class ReflectDemo
{
public static void main(String[] args) throws ClassNotFoundException
{
Date s=new Date();
Class c1=Date.class;
Class c2=s.getClass();
Class c3=Class.forName("java.util.Date");//若该字符串不是类名或者该类名不存在则会抛出异常
System.out.println(c1==c2);
System.out.println(c2==c3);
System.out.println(c1==c3);
System.out.println(c1.isPrimitive());//false
System.out.println(int.class.isPrimitive());//true
System.out.println(int.class==Integer.class);//false
System.out.println(int.class==Integer.TYPE);//TYPE是Integer类中静态变量,它代表Integer包装的基本数据类型
                System.out.println(int[].class.isPrimitive());//int数组类型是继承自Object类,它不是基本数据类型}}
       可以利用Class类中isArray()来判断这个类是否是数组类。System.out.println(int[].class.isArray);
       总之,在源程序中出现的类型,不管是数组类型、void、自定义类还是基本数据类型都有其Class字节码文件——Class对象。

       二、反射

        所谓反射,简单来说就将一个具体的java类(Class类的对象)的各种成分映射成一个java类。这句话的意思是说,在一个java类中,它必定有各种成分,包括:类名、变量、构造函数、成员方法、包等等等。这些成分也是现实中确实存在的一种事物,根据面向对象的思想,我们可以将这些成分封装成一个类。

        比如:我们可以将构造函数封装成一个类,类名叫做:Constructor,这个类专门描述java类中的构造函数,又如:我们将变量封装成一个类,类名叫做:Field,这个类专门描述java类中的变量这一个事物。

        一个具体的类中的一个成员都可以用相应的javaapi中一个反射类的对象来表示,通过调用Class类的方法可以获取到这些对象,所以我们可以通过这些对象对这个具体的类的对象的成员进行操作。

        比如:有一个具体的类A,获取Class对象是A.class,通过Class类的方法获取了构造函数对象,这个对象代表A的构造函数,然后我们就可以通过这个对象的方法来实例化这个A类,因为构造函数本来就是用来实例化产生对象的。

        所以,对这些反射类的使用就是我们学习类的重点。

        三、构造函数类Constructor

        构造函数类Constructor是对一个java类A中的构造函数的描述,我们可以通过Class类的方法获取构造函数对象,然后用构造函数对象的方法来实例化这个类A。
import java.lang.reflect.*;
public class ReflectDemo2
{
public static void main(String[] args) throws Exception
{
//1、获取String的Class类对象,即String类的字节码文件
Class c=Class.forName("java.lang.String");
//2、利用该Class对象获取String类的一个构造函数对象,若要获取多个构造函数,则可用getConstructors()方法来获得一个Constructor[]。
Constructor con=c.getConstructor(byte[].class);//构造函数有重载形式,该方法类部传入构造函数参数类型的Class对象来定位哪个构造函数
//3、利用该构造函数对象初始化String类对象
String s=(String) con.newInstance(new Object[]{"反射练习".getBytes()});//该函数内部传入的是构造函数的初始化值,
System.out.println(s);
}
}
        注意,在使用newInstance这个方法时候,在java1.5之前没有可变参数,其内部传入的是Object[]数组,数组内部是构造函数的参数值,当一个arr[]就是构造函数一个值得时候特别要注意在其外面包一层皮new Object[]{arr[]};且该方法返回的是一个Object对象,所以要强制类型转换。

        四、变量类Field

        变量类Field是对一个java类A中的成员变量的描述,我们可以通过Class类对象的方法获取变量类对象,然后用变量类对象的方法来获取该A类某对象的这个变量的值或者设置这个值。
package udp;
import java.lang.reflect.*;
public class ReflectDemo3
{
public static void main(String[] args) throws Exception
{
Person s=new Person("庄三",22);
//1、获取该对象的类的字节码,即Class的对象
Class c=s.getClass();
//2、调用Class对象的方法getField(String 变量名)来获取变量类的对象,注意这个变量如果是私有的则需要调用getDeclaredField()方法
Field f2 = c.getField("age");
Field f1 = c.getDeclaredField("name");//<span style="font-family: Arial, Helvetica, sans-serif;">若要获取多个构造函数,则可用getFields()方法来获得一个Field[]。</span>
//注意,这个变量私有,上面的方法只是获得私有变量,但是你还是不能访问这个变量,需要调用变量类对象的setAccessible()方法来获取访问权限,这就是所谓暴力反射
f1.setAccessible(true);
//3、调用变量类对象的get(Object)和set(Object ,value)来获取某一个对象该变量的值或者设置该值
System.out.println(f1.get(s));
System.out.println(f2.get(s));//注意,get()放回的是一个Object
f1.set(s, "李四");
f2.set(s, 49);
System.out.println(s.getName()+","+s.getAge());//判断这个变量对象是何种类型的变量用getType()方法来获得该变量类型的字节码文件
}
}

        五、方法类Method

        方法类Method是对一个java类A中的成员方法的描述,我们可以通过Class类对象的方法获取方法类对象,然后用方法类对象的方法来执行该A类对象的这个方法。

package udp;
import java.lang.reflect.*;
public class ReflectDemo4
{
public static void main(String[] args) throws Exception
{
Person s=new Person("zhangsan",20);
//1、获取该类的字节码文件——Class类对象
Class c=s.getClass();
//2、通过该Class类对象的getMethod(方法名,参数类型列表)的方式获取方法类对象,注意空参数的时候该如何处理
Method d1=c.getMethod("setName", String.class);
Method d2=c.getMethod("getName", null);
//3、利用该方法类对象的invoke(哪个对象的方法,传入该方法中的实际参数值)来执行该方法,注意空参数的情况
String ss=(String)d2.invoke(s, null);//若执行该函数有返回值,invoke返回该返回值,以Object的形式!
System.out.println(ss);
d1.invoke(s, "李四");
System.out.println(s.getName());
}
}
        注意,如果是静态的方法的话则invoke()中第一个参数就是为空的。

        六、注意事项

        注意,反射的这些函数中的参数如果是可变参数形式,在GDK1.5以前就是数组的形式来表示,所以你要传入一个数组作为参数的时候请一定小心。你要明白你传入的这个数组是作为可变参数传进去的还是作为一个数组传进去的。

        反射的这些方法返回的值一般都是Object类的,所以一定要强制类型转换!




------Java培训、Android培训、iOS培训、.Net培训、期待与您交流! -------