文章目录
1. 泛型
- 泛型是约束元素的类型
- 泛型符号 T type E element K key V value,实际上泛型的符号可以任意定义
- 繁星符号是一个占位符,先占着位置给引用类型
- 泛型的符号、名称都没有明确的要求
- 泛型符号可以应用在类、接口、方法上
1.2 泛型类
创建一个泛型类,代码如下:
// 在该处指定泛型的个数以及符号
public class Generic<A, B, C> {
private A a;
private B b;
private C c;
public Generic(){
}
public Generic(A a, B b, C c) {
this.a = a;
this.b = b;
this.c = c;
}
public A method1(A a){
return a;
}
}
泛型类的指定需要在类名后指定,如上,指定了 <A,B,C>
后,就在这里进行了类型的占位,在该类中就可以将 A,B,C
当做已经存在的类型进行使用,如传递参数到方法上,指定方法的返回值类型等等。
注意:泛型类中的泛型不能定义在类中的静态方法中,静态方法不能使用泛型符号
静态方法与非静态方法区别如下:
静态方法:是使用
static
关键字修饰的方法,又叫类方法.属于类的,不属于对象, 在实例化对象之前就可以通过类名.方法名调用静态方法。 (静态属性,静态方法都是属于类的,可以直接通过类名调用)。
- 在静态方法中,可以调用静态方法,不能调用非静态方法。
- 在静态方法中,可以引用类变量(即,static修饰的变量),不能引用成员变量(即,没有static修饰的变量)。
- 在静态方法中,不能使用super和this关键字
- 静态方法的生命周期跟相应的类一样长,静态方法和静态变量会随着类的定义而被分配和装载入内存中。一直到线程结束,静态属性和方法才会被销毁。(也就是静态方法属于类)
**非静态方法:**是不含有static关键字修饰的普通方法,又称为实例方法,成员方法。属于对象的,不属于类的。(成员属性,成员方法是属于对象的,必须通过new关键字创建对象后,再通过对象调用)。
- 在普通方法中,可以调用普通方法,可以调用静态方法
- 在普通方法中,可以引用类变量和成员变量
- 在普通方法中,可以使用super和this关键字
- 非静态方法的生命周期和类的实例化对象一样长,只有当类实例化了一个对象,非静态方法才会被创建,而当这个对象被销毁时,非静态方法也马上被销毁。(也就是非静态方法属于对象)
测试如下:
public class Test1 {
public static void main(String[] args) {
// 指定泛型中的类型为具体类型
Generic<String, Integer, Double> generic = new Generic<>();
System.out.println(generic.method1("abcc"));
// 如果不对类型进行指定,那么泛型中定义的类型都是Object类型
Generic generic1 = new Generic();
}
}
1.2 泛型接口
接口中也可以定义泛型,接口中一旦定义了泛型后,就需要将其进行实现,如定义了以下的泛型接口,
public interface GenericInterface<A, B, C> {
A method1(A a);
void method2(B b);
void method3(C c);
}
泛型接口的实现有以下几种方式。
1.2.1 实现类直接确定
直接在实现接口的时候就将其泛型的类型进行确定,代码如下,
public class Generic implements GenericInterface<String, Integer, Double> {
@Override
public String method1(String s) {
return s;
}
@Override
public void method2(Integer integer) {
}
@Override
public void method3(Double aDouble) {
}
}
在上述代码的第一行,实现了对接口泛型的指定,将泛型定义的 A,B,C
类型进行了指定,之后就可以进行正常使用了。
如果类在实现接口的时候不对其进行类型的指定,那么其就都是 Object
类型,如
public class Generic implements GenericInterface {
@Override
public Object method1(Object o) {
return null;
}
@Override
public void method2(Object o) {
}
@Override
public void method3(Object o) {
}
}
1.2.2 实现类同样带泛型
也可以在实现接口的时候将其泛型同样写到类上,这样,相当于就是一个泛型类了,示例如下,
public class Generic<A, B, C> implements GenericInterface<A, B, C> {
@Override
public A method1(A a) {
return a;
}
@Override
public void method2(B b) {
}
@Override
public void method3(C c) {
}
}
测试同泛型类的测试即可。
1.3 泛型方法
泛型方法需要在方法的 public
后面加上 <T>
,这里的 T
为传入的泛型的名称,有多少个就写多少个,如下所示,
public class Generic {
public <T> void method1(T t){
System.out.println(t);
}
}
当然,也可以将返回值设为泛型,如下
public class Generic {
public <T> T method1(T t){
System.out.println(t);
return t;
}
}
同时,静态方法也能够使用泛型,该泛型是传入的泛型,并不是类的泛型,静态方法中是不可以使用类的泛型的,
public class Generic {
public static <T> T method1(T t){
System.out.println(t);
return t;
}
}
测试如下:
public class Test1 {
public static void main(String[] args) {
// 指定泛型中的类型为具体类型
Generic generic = new Generic();
generic.method1("12");
}
}
1.4 泛型上下限
1.4.1 类型通配符
类型通配符一般是使用 ?
代替具体的类型参数。例如 List<?> 在逻辑上是 List,List 等所有 List<具体类型实参>
的父类,如下示例,
public class Test1 {
public static void main(String[] args) {
List<String> name = new ArrayList<String>();
List<Integer> age = new ArrayList<Integer>();
List<Number> number = new ArrayList<Number>();
name.add("icon");
age.add(18);
number.add(314);
getData(name);
getData(age);
getData(number);
}
// 使用类型通配符
public static void getData(List<?> data) {
System.out.println("data :" + data.get(0));
}
}
1.4.2 泛型上下限
比如创建了一个抽象类 Animal
当做父类,有两个子类 Cat
以及 Dog
,Animal
类如下:
public abstract class Animal {
public abstract void eat();
}
Dog
子类如下:
public class Dog extends Animal{
@Override
public void eat() {
System.out.println("狗喜欢吃骨头");
}
}
Cat
子类如下:
public class Cat extends Animal{
@Override
public void eat() {
System.out.println("猫喜欢吃鱼");
}
}
我们定义喂食的方法如下,
public class Generic {
public void feed(List<Animal> animals){
for(Animal animal: animals){
animal.eat();
}
}
}
如果我们想要将一个狗的集合定义的时候传入到 feed
函数中,那么就会报错,如下,
public class Test1 {
public static void main(String[] args) {
List<Dog> animals = new ArrayList<>();
animals.add(new Dog());
animals.add(new Dog());
Generic generic = new Generic();
// 这里会报错
generic.feed(animals);
}
}
如果我们需要能够在泛型中定义后能够添加 Animal
类的子类到泛型中,那么能够怎么做呢?
如果将 feed
函数里面的形参 List<Animal> animals
改为 List<?> animals
,那么就是来者不拒了,什么类型都可以接收,就并非是我们的目的了。
这就涉及到了泛型的上下限了。
泛型的上限如下,
public class Generic {
// 设置泛型的上限,表示这个List里面的值是Animal及Animal的所有子类、孙类等
public void feed(List<? extends Animal> animals){
for(Animal animal: animals){
animal.eat();
}
}
}
测试如下,
public class Test1 {
public static void main(String[] args) {
List<Animal> animals = new ArrayList<>();
animals.add(new Dog());
animals.add(new Dog());
animals.add(new Cat());
Generic generic = new Generic();
generic.feed(animals);
}
}
也可以设置泛型的下限,
public class Generic {
// 设置泛型的下限,表示这个List里面的值是Animal及Animal的父类等
public void feed(List<? super Animal> animals){
for(Animal animal: animals){
animal.eat();
}
}
}
2. 反射
反射就是加载类,并允许以编程的方式解剖类中的各个成分。
反射能够获取的东西:
- 加载类,获取类的字节码:Class对象
- 获取类的构造器:Constructor对象
- 获取类的成员变量:Field对象
- 获取类的成员方法:Method对象
我们创建一个 Student
类,类的代码如下:
public class Student {
private String name;
private int age;
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 int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
2.1 加载类
反射的第一步就是加载类,获取类的字节码,将其加载为 Class
对象,加载类的方式有三种。
我们加载Student
类的方式有如下三种,
public class Test1 {
public static void main(String[] args) throws Exception {
// 加载类方式一
Class c1 = Student.class;
System.out.println("全类名:" + c1.getName());// 获取全类名
System.out.println("类简名" + c1.getSimpleName());// 获取简名
// 加载类方式二
Class c2 = Class.forName("com.mr.study.Student");//这里必须是全类名
System.out.println(c1 == c2);
// 加载类方式三
Student s = new Student();
Class c3 = s.getClass();
System.out.println(c2 == c3);
}
}
2.2 获取构造器
2.2.1 获取构造器
获取构造器的方法如下:
方法名 | 说明 |
---|---|
Constructor<?>[] getConstructors() | 获取全部构造器(只有public部分) |
Constructor<?>[] getDeclaredConstructors() | 获取全部构造器(只要存在就能获取) |
Constructor<T> getConstructor(Class<?>...paramaterTypes) | 获取某个构造器(只有public部分) |
Constructor<T> getDeclaredConstructor(Class<?>...paramaterTypes) | 获取某个构造器(只要存在就能获取) |
我们以上述的 Student
类为加载类,获取其构造器,代码如下,
public class Test1 {
public static void main(String[] args) throws Exception {
// 1.加载类
Class c1 = Student.class;
// 2.获取类的全部构造器
Constructor[] constructors = c1.getDeclaredConstructors();
// 3.遍历数组中的每个构造器对象
for (Constructor constructor: constructors){
// 输出构造器名称和参数个数
System.out.println(constructor.getName() + "---->" + constructor.getParameterCount());
}
//获取某个构造器
Constructor constructor = c1.getDeclaredConstructor(String.class, int.class);
System.out.println(constructor.getName() + "---->" + constructor.getParameterCount());
}
}
2.2.2 使用构造器
获取了构造器后,我们便要使用构造器,使用构造器的方法如下
Constructor提供的方法 | 说明 |
---|---|
T newInstance(Object... initargs) | 调用构造器对象表示的构造器,并初始化对象 |
public void setAccessible(boolean flag) | 设为 true ,表示禁止检查访问控制(暴力反射) |
使用构造器初始化一个对象代码如下,
public class Test1 {
public static void main(String[] args) throws Exception {
// 1.加载类
Class c1 = Student.class;
// 2.获取某个构造器
Constructor constructor = c1.getDeclaredConstructor(String.class, int.class);
// 3.设置禁止访问权限,这样即是是private类型的构造器也能访问到,称为暴力反射
constructor.setAccessible(true);
// 4.使用构造器初始化
Student student = (Student)constructor.newInstance("张三", 24);
System.out.println(student);
}
}
2.3 获取成员变量
2.3.1 获取成员变量
获取成员变量的方法如下:
方法名 | 说明 |
---|---|
public Field[] getFields() | 获取全部成员变量(只有public部分) |
public Field[] getDeclaredFields() | 获取全部成员变量(只要存在就能获取) |
public Field getField(String name) | 获取某个成员变量(只有public部分) |
public Field getDeclaredField(String name) | 获取某个成员变量(只要存在就能获取) |
我们以上述的 Student
类为加载类,获取其成员变量,代码如下,
public class Test1 {
public static void main(String[] args) throws Exception {
// 1.加载类
Class c1 = Student.class;
// 2.获取全部成员变量
Field[] fileds = c1.getDeclaredFields();
// 3.遍历全部成员变量
for (Field filed : fileds) {
System.out.println(filed.getName() + " : " + filed.getType());
}
}
}
2.3.2 使用成员变量
获取了成员变量后,我们便要使用成员变量,使用成员变量的方法如下
方法 | 说明 |
---|---|
void set(Object obj, Object value) | 赋值 |
Object get(Object obj) | 取值 |
public void setAccessible(boolean flag) | 设为 true ,表示禁止检查访问控制(暴力反射) |
使用成员变量赋值并取值成员变量的代码如下,
public class Test1 {
public static void main(String[] args) throws Exception {
// 1.加载类
Class c1 = Student.class;
// 2.获取某个成员变量
Field fname = c1.getDeclaredField("name");
Field fage = c1.getDeclaredField("age");
// 3.创建一个对象
Student s = new Student("张三", 25);
// 4.禁止访问权限
fname.setAccessible(true);
fage.setAccessible(true);
// 5.使用get和set方法
fname.set(s, "李四");//传入需要set的对象以及值
String sName = (String) fname.get(s);
int sAge = (Integer) fage.get(s);
System.out.println("s.name: " + sName);
System.out.println("s.age: " + sAge);
}
}
2.4 获取成员方法
2.4.1 获取成员方法
获取成员方法的方法如下:
方法名 | 说明 |
---|---|
public Method[] getMethods() | 获取全部成员方法(只有public部分) |
public Method[] getDeclaredMethods() | 获取全部成员方法(只要存在就能获取) |
public Method getMethod(Class<?>...paramaterTypes) | 获取某个成员方法(只有public部分) |
public Method getDeclaredMethod(Class<?>...paramaterTypes) | 获取某个成员方法(只要存在就能获取) |
我们以上述的 Student
类为加载类,获取其成员方法,代码如下,
public class Test1 {
public static void main(String[] args) throws Exception {
// 1.加载类
Class c1 = Student.class;
// 2.获取全部成员变量
Method[] methods = c1.getDeclaredMethods();
// 3.遍历全部成员变量
for (Method method : methods) {
System.out.println(method.getName() + " : " + method.getParameterCount() + " : " + method.getReturnType());
}
}
}
2.4.2 使用成员方法
获取了成员方法后,我们便要使用成员方法,使用成员方法的方法如下
方法 | 说明 |
---|---|
public Object invoke(Object obj, Objecct... args) | 赋值 |
public void setAccessible(boolean flag) | 设为 true ,表示禁止检查访问控制(暴力反射) |
使用构造器赋值并取值成员变量的代码如下,
public class Test1 {
public static void main(String[] args) throws Exception {
// 1.加载类
Class c1 = Student.class;
// 2.获取某个成员方法
Method getAge = c1.getDeclaredMethod("getAge");
Method setAge = c1.getDeclaredMethod("setAge", int.class);//方法名和传参类型
// 3.创建一个对象
Student s = new Student("张三", 25);
// 4.禁止访问权限
getAge.setAccessible(true);
setAge.setAccessible(true);
// 5.使用invoke方法
int sAgeBefore = (Integer) getAge.invoke(s);
Object a = setAge.invoke(s, 35);
int sAgeAfter = (Integer) getAge.invoke(s);
System.out.println("s.age Before: " + sAgeBefore);
System.out.println("s.age After: " + sAgeAfter);
}
}