java 反射 继承_Java Core-五.继承-详解反射机制

五.继承

继承 定义:基于已有的类构造新类

反射 定义:在程序运行期间发现更多的类以及属性的能力

多态 定义:一个对象变量可以指示多种实际类型的现象

动态绑定 定义:在运行时能够自动选择调用方法的现象

5.1 类、超类和子类

5.1.2 覆盖方法

规则

超类和子类中的方法签名相同(方法名和参数列表),返回值类型需要保证一样或者是返回值类型的子类(协变返回类型)

覆盖和重载的区别

覆盖是方法签名相同

重载是方法名相同,参数列表必须不同,对返回类型,访问修饰符,异常声明没有任何限制

5.1.6 方法调用流程

图解

139588533d401cb2b02bcd108981d8b6.png

5.1.7 阻止继承:final类和方法

规则

类用final修饰后,无法被继承,其中的方法也自动用final修饰。域不包括

方法用final修饰后,子类无法覆盖当前方法

5.1.8 强制类型转换

注意

强制类型转换前,使用instanceof方法判断是否为所属类型

5.2 Object:所有类的超类

注意:只有基本类型不是对象

5.2.1 equals

特点

自反性:x为任何非空引用,x.equals(x)应该返回true

对称性:对于任何引用x,y,当且仅当x.equals(y)返回true,则y.equals(x)返回也为true

传递性:对于任何引用x,y,z,如果x.equals(y)返回true,y.equals(z)返回true,则x.equals(z)返回也为true

一致性:如果x,y引用的对象没有变化,则x.equals(y)应一直返回true

equals和等号的区别——重点

等号(==)

基本数据类型(也称原始数据类型) :byte,short,char,int,long,float,double,boolean。他们之间的比较,应用双等号(==),比较的是他们的值。

引用数据类型:当他们用(==)进行比较的时候,比较的是他们在内存中的存放地址(确切的说,是堆内存地址)

equals

方法的初始默认行为是比较对象的内存地址值

在一些类库当中这个方法被重写了,如String、Integer、Date,比较对象的成员变量值是否相同

合理的equals重写逻辑

显示参数声明为Object

判断是否引用同一对象

判断是否为空

判断是否属于同一类

将比较对象转换成相应类型的变量

域的比较

5.2.3 hashCode方法

散列码(hash code) 定义:是由对象导出的一个整型值

注意

字符串和基本数据类型的包装类创建的对象存在hashCode相同的情况,因为是由内容导出的

public class TestHashCode {

public static void main(String[] args) {

String s1 = "a";

String s2 = "a";

Integer i = 10;

Integer k = 10;

System.out.println(s1.hashCode());

System.out.println(s2.hashCode());

System.out.println(s1.equals(s2));

System.out.println(i.hashCode());

System.out.println(k.hashCode());

System.out.println(i.equals(k));

}

}/* output

97

97

true

10

10

true

*/

如果重新定义equals方法,就必须重新定义hashCode方法,以便用户可以将对象插入到散列表中。如果重新定义,会出现equals相等,hashCode不等——考点

5.2.4 toString方法

用途:返回表示对象值的字符串

注意

toString方法常见的原因:当对象与一个字符串通过操作符+连接,编译将自动使用toString方法

public class TestToString {

String name = "asd";

public static void main(String[] args) {

TestToString testToString = new TestToString();

System.out.println(testToString+"jkl");

}

}/* output

Five.TestToString.TestToString@1218025cjkl

*/

建议重写toString方法,默认调用的方法可读性较差

public class TestToString {

String name = "asd";

@Override public String toString() {

return "TestToString{" + "name='" + name + '\'' + '}';

}

public static void main(String[] args) {

TestToString testToString = new TestToString();

System.out.println(testToString+"jkl");

}

}/* output

TestToString{name='asd'}jkl

*/

5.2.5 getClass方法

用途:返回包含对象信息的类对象

public class TestToString {

String name = "asd";

public static void main(String[] args) {

TestToString testToString = new TestToString();

System.out.println(testToString.getClass());

}

}/* output

class Five.TestToString.TestToString

*/

5.3 泛型数组列表

泛型 定义:Java 泛型的参数只可以代表类,不能代表个别对象

1.ArrayList

定义:采用类型参数的泛型类

// 使用方法

ArrayList staff = new ArrayList();

// 泛型的类型为Employee

规则

使用add方法增加新数据。如果空间用尽,则自动创建更大的数组,并将原数据拷贝到更大的数组中

使用size方法获取实际元素数量

使用trimToSize将清除多余的存储空间

使用get和set方法访问和设置元素

注意

数组列表和数组大小的核心区别:数组分配100的空间是已经在内存中存在的;数组列表在仅是具备存储100的潜力,即使是完成了初始化之后

使用时,指定泛型的类型。因为不指定泛型类型,存储和修改数据接受任意类型对象,因为使用的是Object

5.4 对象包装器和自动装箱

1.自动装箱

装箱 定义:基本数据类型变换为基本类型的包装类

// 装箱

public class TestAutoBoxing {

public static void main(String[] args) {

ArrayList list = new ArrayList();

list.add(5); // 等于 list.add(Integer.valueOf(5));

}

}/* conclusion

1.list中的元素为Integer类型

2.当使用.add()方法添加时,被添加的类型为基本类型int

3.因此将自动变换为 list.add(Integer.valueOf(5))

*/

注意

自动装箱规范要求boolean,byte,char小于等于127,介于-128至127之间的short和int被包装到固定的对象中。

原因是:IntegerCache.low 默认是-128;IntegerCache.high默认是127,超出范围的将创建对象存储,不然直接返回值。减少new,减轻jvm压力。

public class TestAutoBoxing {

public static void main(String[] args) {

// 使用包装类

Integer a = 127;

Integer b = 127;

Integer c = 128;

Integer d = 128;

System.out.println("a == b:"+(a == b));

System.out.println("c == d:"+(c == d));

}

}/* output

a == b:true

c == d:false

*/

由于包装器类引用可以为null,因此会出现NullPointerException

如果混合使用Integer和Double,Integer会拆箱,提升为double,然后装箱为Double

自动拆箱和自动装箱是编译器认可的,不是虚拟机。

2.自动拆箱

拆箱 定义:将包装类数据拆成基本类型数据

public class TestAutoBoxing {

public static void main(String[] args) {

// 装箱

ArrayList list = new ArrayList();

list.add(5);

// 拆箱

int intType = list.get(0); // 等于 int intType = list.get(0).intValue();

}

}/* conclusion

1.list中的元素为Integer类型

2.当使用.get()方法获取时,获得的值类型为Integer

3.因此将自动变换为 int intType = list.get(0).intValue();

*/

5.5 参数可变的方法

参数可变 定义:支持用可变的参数数量调用的方法

格式

// 使用 ...

Double ... args

// 同时接收多个参数

System.out.printf("%d %s",n,"name");

// 底层实现代码

public PrintStream printf(String format, Object ... args) {

return format(format, args);

}

/* conclusion

1.“String format”为格式字符串,“Object ... args”为Object对象的数组,因此数量可变

*/

public class TestFindMax {

static void findMax(Object ... args){

double largest = Double.NEGATIVE_INFINITY;

for (Object y: args

) {

Double z = (Double) y;

if (z > largest)

largest = z;

}

System.out.println(largest);

}

public static void main(String[] args) {

TestFindMax.findMax(3.454,34.3);

}

}

5.6 枚举类

格式

enum EnumName{

MAX,MIN,DEDIUM;

}

注意

枚举类中是实例,因此能够创建实例中的变量,但必须使用构造方法赋值

public class TestEnum {

public enum Size{

MAX("max",3),MIN("min",1),MEDIUM("medium",2);

// 成员变量

private String name;

private int num;

// 成员变量的构造方法

Size(String name , int i) {

this.name = name;

this.num = i;

}

}

public static void main(String[] args) {

// 在同一个类中,因此可以访问私有的成员变量name

String name = Size.MAX.name;

System.out.println(name);

}

}

5.7 反射——重点

反射 定义:支持分析类的能力的程序

反射机制:将类中的各个部分封装成其他对象

原理图

d307a47357dafa3cc9996bf2d4e8ba12.png

获取Class对象的三种方式

Class.forName("全类名"):在第一阶段,将字节码文件加载进内存,然后获取Class对象

多用于配置文件

类名.class:在第二阶段,通过类名的class属性

多用于参数的传递

对象.getClass():在第三阶段,通过Object的getClass方法获取

多用于对象的字节码的获取

public class TestReflectionClass {

public static void main(String[] args) throws Exception {

/*

Class对象获取的三种方式

1.通过Class.forclass("全类名")获取

2.通过类名.class()获取

3.通过对象.getClass()方法

*/

// 1.通过Class.forclass("全类名")获取

Class cls1 = Class.forName("Five.TestReflection.Person");

System.out.println(cls1);

// 2.通过类名.class()获取

Class cls2 = Person.class;

System.out.println(cls2);

// 3.通过对象.getClass()方法

Person p = new Person();

Class cls3 = p.getClass();

System.out.println(cls3);

// 4.比较三个class引用所指向的是否为同一个Class对象

System.out.println("比较三个class引用所指向的是否为同一个Class对象");

System.out.println("cls1 == cls2:"+(cls1 == cls2));

System.out.println("cls1 == cls2:"+(cls1 == cls3));

}

}/* output

class Five.TestReflection.Person

class Five.TestReflection.Person

class Five.TestReflection.Person

比较三个class引用所指向的是否为同一个Class对象

cls1 == cls2:true

cls1 == cls2:true

*/

注意

同一个字节码文件(.class)在一次程序运行中只加载一次,所以三种获取Class对象的方式获取到的Class对象都是同一个

用途

在运行时分析类的能力

在运行时操作对象

Core

实现通用的数组操作代码

利用Mehtod对象,类似C++中的函数指针

5.7.1 Class类

1.Class对象中的功能

1.1获取成员变量值

规则

Field[] getFields():获取所有的public修饰的成员变量(包括超类)

Field getField():获取指定的public修饰的成员变量(包括超类)

Field[] getDeclaredFields():获取所有的成员变量(即使是private修饰,暴力反射)

Field getDeclaredField():获取指定的成员变量(即使是private修饰,暴力反射)

注意

getFields和getDeclaredFields()获取时,getFields能够同时获取到超类和子类中的public变量,getDeclaredFields()只能获取到子类中的所有访问修饰类型的变量

// 超类

public class Father {

// 成员变量

private String priFatherName;

private int priFatherAge;

public String pubFatherName;

public int pubFatherAge;

}

// 子类

public class Son extends Father {

// 成员变量

private String priSonName;

private int priSonAge;

public String pubSonName;

public int pubSonAge;

}

// 测试类

package Five.TestReflection;

import java.lang.reflect.Field;

public class TestField {

public static void main(String[] args) throws Exception {

/*

getField和getDeclaredField

*/

// getField

System.out.println("--测试getField");

Field[] field1 = Son.class.getFields();

for (Field f : field1) {

System.out.println(f);

}

// getDeclaredField

System.out.println("--测试getDeclaredField");

Field[] field2 = Son.class.getDeclaredFields();

for (Field f : field2) {

System.out.println(f);

}}}

/* output

--测试getField

public java.lang.String Five.TestReflection.Son.pubSonName

public int Five.TestReflection.Son.pubSonAge

public java.lang.String Five.TestReflection.Father.pubFatherName

public int Five.TestReflection.Father.pubFatherAge

--测试getDeclaredField

private java.lang.String Five.TestReflection.Son.priSonName

private int Five.TestReflection.Son.priSonAge

public java.lang.String Five.TestReflection.Son.pubSonName

public int Five.TestReflection.Son.pubSonAge

*/

Field对象方法

get(Object obj):返回的 Field表示字段的值,指定对象上

set(Object obj, Object value):设置域为代表的这 Field对象指定对象上的参数指定的新价值

setAccessible():忽略访问修饰符的安全检查,也称为暴力反射(如果需要get或者set使用private修饰的变量,则需要使用该方法),用于调试,持久存储,相似机制。

// 超类

public class Father {

// 成员变量

private String priFatherName;

private int priFatherAge;

public String pubFatherName;

public int pubFatherAge;

}

// 子类

public class Son extends Father {

// 成员变量

private String priSonName;

private int priSonAge;

public String pubSonName;

public int pubSonAge;

}

// 测试类

package Five.TestReflection;

import java.lang.reflect.Field;

public class TestField {

public static void main(String[] args) throws Exception {

/*

get和set方法

*/

Son son = new Son();

// get,public

System.out.println("--测试get方法,使用getField,作用于public修饰对象");

Field field3 = Son.class.getField("pubSonName");

Object value3 = field3.get(son);

System.out.println(value3);

// get,private

System.out.println("--测试get方法,使用getField,作用于private修饰对象");

// 由于getField只能作用于public修饰的成员,因此无法访问

// Field field4 = Son.class.getField("priSonName");

// field4.setAccessible(true);

// Object value4 = field4.get(son);

// System.out.println(value4);

System.out.println("失败");

// get,private

System.out.println("--测试get方法,使用getDeclaredField,作用于private修饰对象");

Field field5 = Son.class.getDeclaredField("priSonName");

// 获取前需要忽略访问的安全检查

field5.setAccessible(true);

Object value5 = field5.get(son);

System.out.println(value5);

// set,public

System.out.println("--测试set方法,使用getField,作用于public修饰对象");

Field field6 = Son.class.getField("pubSonName");

field6.set(son, "Toyz");

Object value6 = field6.get(son);

System.out.println(value6);

// set,private

System.out.println("--测试set方法,使用getDeclaredField,作用于private修饰对象");

Field field7 = Son.class.getDeclaredField("priSonName");

// 获取前需要忽略访问的安全检查

field7.setAccessible(true);

Object value7 = field7.get(son);

System.out.println("修改前,priSonName:"+value7);

field7.set(son, "QQ");

value7 = field7.get(son);

System.out.println("修改前,priSonName:"+value7);

}

}

/* output

--测试get方法,使用getField,作用于public修饰对象

null

--测试get方法,使用getField,作用于private修饰对象

失败

--测试get方法,使用getDeclaredField,作用于private修饰对象

null

--测试set方法,使用getField,作用于public修饰对象

Toyz

--测试set方法,使用getDeclaredField,作用于private修饰对象

修改前,priSonName:null

修改前,priSonName:QQ

*/

1.2 获取构造方法

规则

getConstructor(类>... parameterTypes):获取public修饰的指定构造方法(不含超类)

getConstructors():获取public修饰的所有构造方法(不含超类)

getDeclaredConstructor(类>... parameterTypes):获取指定构造方法(包括private修饰,暴力反射,不含超类)

getDeclaredConstructors():获取所有构造方法(包括private修饰,暴力反射,不含超类)

获取构造方法,无法获得超类,原因是构造方法无法被继承,因此无法获取

// 超类

public class Father {

public Father(String priFatherName , int priFatherAge , String pubFatherName ,

int pubFatherAge) {

this.priFatherName = priFatherName;

this.priFatherAge = priFatherAge;

this.pubFatherName = pubFatherName;

this.pubFatherAge = pubFatherAge;

}

public Father() { }

private Father(String priFatherName , int priFatherAge){

this.priFatherName = priFatherName;

this.priFatherAge = priFatherAge;

}

}

// 子类

public class Son extends Father {

public Son(String priSonName , int priSonAge , String pubSonName , int pubSonAge) {

this.priSonName = priSonName;

this.priSonAge = priSonAge;

this.pubSonName = pubSonName;

this.pubSonAge = pubSonAge;

}

public Son(){}

private Son(String priSonName , int priSonAge){

this.priSonName = priSonName;

this.priSonAge = priSonAge;

}

}

// 测试类

public class TestConstructor {

public static void main(String[] args) throws Exception {

/*

getConstructor和getDeclaredConstructor

*/

// getConstructor,无参构造器和有参构造器

System.out.println("--测试getConstructor");

Constructor constructor1 = Son.class.getConstructor();

System.out.println("无参构造器:"+constructor1);

Constructor constructor2 = Son.class.getConstructor(String.class,int.class,String.class,int.class);

System.out.println("有参构造器:"+constructor2);

// getConstructors

System.out.println("--测试getConstructors");

Constructor[] constructors3 = Son.class.getConstructors();

for (Constructor c : constructors3) {

System.out.println(c);

}

// getDeclaredConstructor

System.out.println("--测试getDeclaredConstructor");

Constructor constructor4 = Son.class.getDeclaredConstructor(String.class,int.class);

System.out.println(constructor4);

// getDeclaredConstructors

System.out.println("--测试getDeclaredConstructors");

Constructor[] constructor5 = Son.class.getDeclaredConstructors();

for (Constructor c : constructor5){

System.out.println(c);

}

}

}

/* output

--测试getConstructor

无参构造器:public Five.TestReflection.Son()

有参构造器:public Five.TestReflection.Son(java.lang.String,int,java.lang.String,int)

--测试getConstructors

public Five.TestReflection.Son()

public Five.TestReflection.Son(java.lang.String,int,java.lang.String,int)

--测试getDeclaredConstructor

private Five.TestReflection.Son(java.lang.String,int)

--测试getDeclaredConstructors

private Five.TestReflection.Son(java.lang.String,int)

public Five.TestReflection.Son()

public Five.TestReflection.Son(java.lang.String,int,java.lang.String,int)

*/

Constructor对象方法

newInstance(Object... initargs):利用这 Constructor对象创建和初始化的构造函数的声明类的一个新实例构造函数,用指定的初始化参数。

// 测试类

public class TestConstructor {

public static void main(String[] args) throws Exception {

/*

newInstance(Object... initargs)

*/

// newInstance(Object... initargs)无参构造器

System.out.println("--newInstance(Object... initargs) 无参构造器");

Constructor constructor6 = Son.class.getConstructor();

System.out.println(constructor6.newInstance());

// newInstance(Object... initargs)含参构造器

System.out.println("--newInstance(Object... initargs) 含参构造器");

Constructor constructor7 = Son.class.getDeclaredConstructor(String.class,int.class);

constructor7.setAccessible(true); // 忽略访问的安全检查

System.out.println(constructor7.newInstance("Toyz",44));

}

}

/* output

--newInstance(Object... initargs) 无参构造器

Son{priSonName='null', priSonAge=0, pubSonName='null', pubSonAge=0}

--newInstance(Object... initargs) 含参构造器

Son{priSonName='Toyz', priSonAge=44, pubSonName='null', pubSonAge=0}

*/

1.3获取方法

规则

getMethod(String name, 类>... parameterTypes):获取public修饰的指定方法(含超类)

getMethods():获取public修饰的指定方法(含超类)

getDeclaredMethod(String name, 类>... parameterTypes):获取指定方法(包括private修饰,暴力反射,不含超类)

getDeclaredMethods():获取所有方法(包括private修饰,暴力反射,不含超类)

// 超类

public class Father {

// 方法

public void eat(){

System.out.println("father eat...");

}

public void eat(String food){

System.out.println("father eat..."+food);

}

public void edu(){

System.out.println("father edu...");

};

private void run(){

System.out.println("father run...");

}

}

// 子类

public class Son extends Father {

// 方法

public void eat(){

System.out.println("son eat...");

}

public void eat(String food){

System.out.println("son eat..."+food);

}

private void run(){

System.out.println("son run...");

}

}

// 测试类

public class TestMethod {

public static void main(String[] args) throws Exception {

/*

getMethod和getDeclaredMethod

*/

Son son = new Son();

// getMethod,空参方法

System.out.println("--测试getMethod,空参方法");

Method method1 = Son.class.getMethod("eat");

System.out.println(method1);

// getMethods,所有方法

System.out.println("--测试getMethods");

Method[] method2 = Son.class.getMethods();

for (Method m : method2) {

System.out.println(m);

}

// getDeclaredMethods,所有方法

System.out.println("--测试getDeclaredMethods");

Method[] method3 = Son.class.getDeclaredMethods();

for (Method m : method3) {

System.out.println(m);

}}}

/* output

--测试getMethod,空参方法

public void Five.TestReflection.Son.eat()

--测试getMethods

public java.lang.String Five.TestReflection.Son.toString()

public void Five.TestReflection.Son.eat(java.lang.String)

public void Five.TestReflection.Son.eat()

public void Five.TestReflection.Father.edu()

public final native void java.lang.Object.wait(long) throws java.lang.InterruptedException

public final void java.lang.Object.wait(long,int) throws java.lang.InterruptedException

public final void java.lang.Object.wait() throws java.lang.InterruptedException

public boolean java.lang.Object.equals(java.lang.Object)

public native int java.lang.Object.hashCode()

public final native java.lang.Class java.lang.Object.getClass()

public final native void java.lang.Object.notify()

public final native void java.lang.Object.notifyAll()

--测试getDeclaredMethods

private void Five.TestReflection.Son.run()

public java.lang.String Five.TestReflection.Son.toString()

public void Five.TestReflection.Son.eat(java.lang.String)

public void Five.TestReflection.Son.eat()

*/

Method对象方法

invoke(Object obj, Object... args):第一个参数为隐式参数,静态方法时为null。调用底层的方法,这 方法对象表示,对指定对象的指定参数。能够调用超类的方法

// 测试类

public class TestMethod {

public static void main(String[] args) throws Exception {

/*

invoke

*/

// invoke,含参子类public方法

System.out.println("--测试invoke,含参子类public方法");

Method method4 = Son.class.getMethod("eat",String.class);

method4.invoke(son, "Fish");

// invoke,含参父类public方法

System.out.println("--测试invoke,含参父类public方法");

Method method5 = Son.class.getMethod("edu");

method5.invoke(son);

// invoke,无参子类private方法

System.out.println("--测试invoke,无参子类private方法");

Method method6 = Son.class.getDeclaredMethod("run");

method6.setAccessible(true);

method6.invoke(son);

}

}

/* output

--测试invoke,含参子类public方法

son eat...Fish

--测试invoke,含参父类public方法

father edu...

--测试invoke,无参子类private方法

son run...

*/

2.Practice(反射)

要求:写一个“框架”在不改动代码的前提下,通过配置文件实现创建任意类的对象,执行任意方法

步骤

将需要创建的类和执行的方法写在配置文件中

在程序中读取配置文件

使用反射将类加载进内存

创建对象

执行方法

实现

创建类(配置文件需要用到的示范类)

package Five.TestReflection;

public class Father {

public Father() { }

// 方法

public void eat(){

System.out.println("father eat...");

}

}

创建配置文件

className=Five.TestReflection.Father

methodName=eat

读取并执行

package Five.TestReflection;

import java.io.InputStream;

import java.lang.reflect.Method;

import java.util.Properties;

public class TestReflection {

public static void main(String[] args) throws Exception {

// 1.加载配置文件

// 1.1创建pro对象

Properties properties = new Properties();

// 1.2加载配置文件,转换为集合

// 1.2.1获取配置文件路径

ClassLoader classLoader = TestReflection.class.getClassLoader();

InputStream is = classLoader.getResourceAsStream("pro.properties");

properties.load(is);

// 2.获取配置文件中定义的数据

String className = properties.getProperty("className");

String methodName = properties.getProperty("methodName");

// 3.加载类进内存

Class cls1 = Class.forName(className);

// 4.创建对象-反射

Object object = cls1.newInstance();

// 5.执行方法-反射

Method method1 = cls1.getMethod(methodName);

method1.invoke(object);

}

}

/* output

father eat...

*/

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值