不同的数据结构可以用同样的操作就是泛型
1)定义
/**
* 要使用泛型的类加上类似与通配符的,中间字符可以任意。也可以传入多个类型
*/
class Person<T> {
//内部属性类型,使用T代替
private T age;
//内部方法参数类型,使用T代替
public void setAge(T age) {
this.age = age;
}
//内部方法返回类型,使用T代替
public T getAge() {
return this.age;
}
}
2)调用
public class Generics {
public static void main(String args[]) {
//创建对象的时候传入类型,如果是两个则<>里面放两个类型
Person<String> p = new Person<String>();
p.setAge("3 years old");
//System.out.println(p.getAge());
printInfo(p);
//<>内只能用类,所以不能用基础数据类型int要用Integer类代替
Person<Integer> p2 = new Person<Integer>();
p2.setAge(3);
//System.out.println(p2.getAge());
printInfo(p2);
Person<?> p3;
//通用引用p3赋值
p3 = p;
//不能设置(类型冲突)
//p3.setAge("4 years");
//但可以获取
p3.getAge();
}
//Person>是通配符,表示传进来可以是String类也可以是Integer类
public static void printInfo(Person<?> p) {
System.out.println(p.getAge());
}
}
1)定义
//定义格式返回值前面,参数里都要有相应的
public static <T> void printInfo2(Person<T> p) {
System.out.println(p.getAge());
}
2)调用
//p为具体的对象Person p = new Person();
printInfo2(p);
printInfo2(p2);
printInfo2(p3);
1)定义
//子类继续使用泛型
class Student<T> extends Person<T> {
}
//子类不再使用泛型
class Student2 extends Person<String> {
//子类确定类型为String,所以前面也就不需要加,同时对应父类的类型也已被确定
}
2)调用
//创建子类对象,传入类型
Student<Integer> s = new Student<Integer>();
//调用父类的方法
s.setAge(10);
//向上转换
printInfo(s);
//已经确定类型,不需传入
Student2 s2 = new Student2();
//对应父类的类型已被确定,调用父类方法直接传入String
s2.setAge("11 years");
printInfo(s2);
1)定义
interface Person<T> {
public void setAge(T age);
public T getAge();
}
//子类继续使用泛型
class Student<T> implements Person<T> {
T age;
public void setAge(T age){
this.age = age;
}
public T getAge() {
return this.age;
}
}
//指定对应接口的泛型类型
class Student2 implements Person<String> {
//已经确定了类型,直接使用
String age;
public void setAge(String age){
this.age = age;
}
public String getAge() {
return this.age;
}
}
public static void main(String args[]) {
//创建子类对象,传入类型
Student<Integer> s = new Student<Integer>();
s.setAge(10);
//向上转换,子类里实现了方法,同时接口没有实现对应的方法,所以里面调用的是子类的方法
printInfo(s);
//已经确定类型,不需传入
Student2 s2 = new Student2();
s2.setAge("11 years");
printInfo(s2);
}
public static void printInfo(Person<?> p) {
System.out.println(p.getAge());
}
声明泛型的时候可以指定泛型的上限和下限
1)泛型的上限: T只能是Number类或其子类
//T只能是Number类或其子类Integer, Float等
class Student<T extends Number> implements Person<T> {
T age;
public void setAge(T age){
this.age = age;
}
public T getAge() {
return this.age;
}
}
2)泛型的下限: super String> T只能是String类或其父类
//super只能使用通配符,不能直接在名字里使用(即只能在使用这个泛型的时候再指定)
class Student<T> implements Person<T> {
T age;
public void setAge(T age){
this.age = age;
}
public T getAge() {
return this.age;
}
}
//使用的时候指定下限(用通配符?指定下限)。传进来 只能是String类或其父类
public static void printInfo(Person<? super String> p) {
System.out.println(p.getAge());
}
//调用
Student<String> s = new Student<String>();
s.setAge("10");
printInfo(s);
正常步骤我们是import"包.类"然后通过new实例化最后得到实例化对象。
那么能否反过来呢?
答案是肯定的。我们可以从实例化对象得到getClass方法最后得到完整的"包.类"名称,这个反过来的操作(根据实例化对象得到完整的"包.类"名称)就是所谓的反射操作(当然反射的作用不仅仅是得到完整的"包.类"名称)
注意:在反射操作中,一切的操作都使用Object完成,类和数组的引用都可以使用object进行接收
Person p=new Person();
//打印"包.类"名称
System.out.println(p.getClass().getName());
JVM会加载*.Class文件(Java写的类)到内存里,也就是会在内存里创建一个class object用来描述这个类,包括类的包、类名称、构造方法、方法和属性等,这样就可以使用这个class object来实例化对象(在内存里对于一个class只有一个class object,这个class object是用来描述类本身的,我们可以使用这个class object来创建实例化对象),可以有三种方法获得一个类的class object。
//1).Class> c=Class.forName("包.类");
Class<?> c1 = null;
try {
c1 = Class.forName("a.b.c.d.Person");
} catch (ClassNotFoundException e) {
System.out.println(e);
}
System.out.println(c1.getName());
//2).Class> c = new X().getClass();
Person p = new Person();
Class<?> c2 = p.getClass();
System.out.println(c2.getName());
//3).Class> c=X.class;
Class<?> c3 = Person.class;
System.out.println(c3.getName());
对于数组或者其他数据类型,也有对应的类,对应的class object
int arr[] = {1,2,3};
int arr2[] = {1,2,3,4};
int arr3[][] = {{1,2,3,4},{1}};
Class<?> c4 = arr.getClass();
Class<?> c5 = arr2.getClass();
Class<?> c6 = arr3.getClass();
System.out.println(c4.getName());
System.out.println(c5.getName());
System.out.println(c6.getName());
//同样的数组类型是一样的class
System.out.println((c4 == c5));
//二维数组和一维数组不一样所以不一样的class
System.out.println((c4 != c6));
//基本的数据类型也是一个类,里面也有class
Class<?> c7 = int.class;
System.out.println(c7.getName());
使用反射来获取类的实例化对象(类的属性与方法),取代import a.b.c.d.Person;(的形式)
//Exception是其他异常类的父类,所以可以这样代替InstantiationException等其他异常
public static void main(String args[]) throws Exception {
Class<?> c = null;
try {
//使用名字来获得这个Person的class object
c = Class.forName("a.b.c.d.Person");
} catch (ClassNotFoundException e) {
System.out.println(e);
}
//然后创建它的实例化对象(Object是所有类的父类,所以可以这么写)
Object p = null;
try {
//然后创建它的实例化对象(Object是所有类的父类,所以可以这么写(可以向上转换)),实际是调用了那个类的无参构造方法
p = c.newInstance();
} catch (InstantiationException e) {
System.out.println(e);
}
//调用有参构造方法的实例化
//获得参数是String的构造方法的class
Constructor<?> con = c.getConstructor(String.class);
//然后创建它的实例化对象,有参数
Object p2 = con.newInstance("123");
}
//传入方法名称(该方法在定义的地方要有public权限)和参数类型
Method set = c.getMethod("setName", String.class);
//调用实例化对象中的方法,要设置的实例化对象,然后才是传入的值
set.invoke(p2, "123");
set.invoke(p, "abc");
//对于静态方法, invoke的第1个参数可以写为null
Method get = c.getMethod("getName");//没有传入参数
System.out.println(get.invoke(p));
System.out.println(get.invoke(p2));
set.invoke(p2, "123");//调用实例化对象中的方法,要设置的实例化对象,然后才是传入的值
set.invoke(p, "abc");
//对于静态方法, invoke的第1个参数可以写为null
Method get = c.getMethod("getName");//没有传入参数
System.out.println(get.invoke(p));
System.out.println(get.invoke(p2));
1).最好是用上面的方式,通过方法来访问类的属性
2).也可以直接获取属性:
//(1)获得公共属性, 此方法先搜本类, 再搜它实现的接口,最后在父类中搜索
Field f = c.getField(String name);
//(2)可以获得类里面所有属性(包括private,public等)中名为name的属性
Field name = c.getDeclaredField("name");
//设置为可访问,如果要访问的属性为public可以不要这句话,其实设置为可访问也就破坏了类的封装性,所以一 般不使用这种方法,而是去调用那个类的设置获取方法(即set方法)
name.setAccessible(true);
//设置某个实例化对象中的name属性
name.set(p, "www");
name.set(p2, "123");
//获得某个对象的name属性
System.out.println(name.get(p));
System.out.println(name.get(p2));
增加程序灵活性
通过类的名称(放在文件里或者通过参数传递,这就不用在代码中写死,传入什么就可以实例化什么),然后就可以实例化出不同的对象