- 子类继承父类(extends)
- 类实现接口(implements)
核心之处就在于对父类方法的改写或对接口方法的实现,以取得在运行时不同的执行效果。要使用多态,在声明对象时就应该遵循一条法则:声明的总是父类类型或接口类型,而创建的是实际类型.
以ArrayList为例子,要使用多态的特性,要按照如下方式定义
List list = new ArrayList();
此外,在定义方法参数时也通常总是应该优先使用父类类型或接口类型,例如:
public void test(List list);
这样声明最大的好处在于它的灵活性,假如某一天ArrayList无法满足要求,我们希望用LinkedList来代替它,那么只需要在对象创建的地方把new ArrayList()改为new LinkedList即可,其它代码一概不用改动。
---------------------------不定时增加------------------------
反射 :
定义如下:java程序在运行状态中,对于任意一个类,都能够在运行时知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意方法和属性;这种动态获取信息以及动态调用对象方法的功能称为java语言的反射机制。
问题1:什么叫获取一个方法,什么叫获取一个属性?
这是一个很好的问题,理解了这个问题之后也就不会觉得反射过于抽象了。java当中提供了专门的方法的抽象Method类和专门的属性的抽象Field类,以及专门的所有类的抽象Class类,并提供了一系列的方法,来帮助我们获取一个类的属性和方法。获取到的属性和方法,将以普通对象的方式存在。与我们自己写的类并无任何区别。下面是一个代码例子:
public class MethodClass {
public static void main(String[] args) throws Exception {
//1.获取Class对象
Class xxxClass = Class.forName("com.XXX.XXX");
//2.获取所有公有方法
Method[] methodArray = xxxClass .getMethods();
//3.获取字段
Field[] fieldArray = xxxClass .getFields();
}}
反射原理:
Class类与java.lang.reflect库一起对反射的概念提供了技术支持。java.lang.reflect类库包含了Field类,Method类以及Constructor类。这些类用来表示未知类里对应的成员。Class类提供了获取getFields()、getMethods()和getConstructors()等方法,而这些方法的返回值类型就定义在java.lang.reflect当中。
如果不知道某个对象的确切类型(即list引用到底是ArrayList类型还是LinkedList类型),RTTI可以告诉你,但是有一个前提:这个类型在编译时必须已知,这样才能使用RTTI来识别它。
要想理解反射的原理,必须要结合类加载机。反射机制并没有什么神奇之处,当通过反射与一个未知类型的对象打交道时,JVM只是简单地检查这个对象,看它属于哪个特定的类,然后再通过拿到的某一个类的全限定名去找这个类的Class文件 。因此,那个类的.class对于JVM来说必须是可获取的,要么在本地机器上,要么从网络获取。所以对于RTTI和反射之间的真正区别只在于:
RTTI,编译器在编译时打开和检查.class文件
反射,运行时打开和检查.class文件
对于反射机制而言.class文件在编译时是不可获取的,所以是在运行时获取和检查.class文件。
总结起来说就是,反射是通过Class类和java.lang.reflect类库一起支持而实现的,其中每一个Class类的对象都对应了一个类,这些信息在编译时期就已经被存在了.class文件里面了,Class 对象是在加载类时由 Java 虚拟机以及通过调用类加载器中的defineClass方法自动构造的。对于我们定义的每一个类,在虚拟机中都有一个应的Class对象。
那么在运行时期,无论是通过字面量还是forName方法获取Class对象,都是去根据这个类的全限定名(全限定名必须是唯一的,这也间接回答了为什么类名不能重复这个问题。)然后获取对应的Class对象
总结: java虚拟机帮我们生成了类的class对象,而通过类的全限定名,我们可以去获取这个类的字节码.class文件,然后再获取这个类对应的class对象,再通过class对象提供的方法结合类Method,Filed,Constructor,就能获取到这个类的所有相关信息. 获取到这些信息之后,就可以使用Constructor创建对象,用get和set方法读取和修改与Field对象相关的字段,用invoke方法调用与Method对象关联的方法。
反射机制非常重要,应用也非常之广泛。在使用反射时,我们的代码里面可以出现任何一个具体的构造器,字段信息,方法,但是却能动态的生成对象,调用他们的方法,这是一个非常通用的功能,由此带来的价值也是惊人的。反射比较出名的应用有:
1. Spring/Mybatis等框架,行内有一句这样的老话:反射机制是Java框架的基石。最经典的就是xml的配置模式。
2. JDBC 的数据库的连接
3. 动态生成对象,应用于工厂模式中. spring的bean容器也就是一个工厂
4. jdk动态代理
利用反射获取传入接口的实现类
5. 注解机制的实现
利用反射可以获取每一个filed,Filed类提供了getDeclaredAnnotations方法以数组形式返回这个字段所有的注解....
6. 编辑器代码自动提示的实现
等等
反射样例:
反射应用实例
下面的代码是使用反射获取方法的例子程序
model类
package com.dr.Reflection.getMethodByReflect;
public class Student {
//**************成员方法***************//
public void show1(String s) {
System.out.println("调用了:公有的,String参数的show1(): s = " + s);
}
protected void show2() {
System.out.println("调用了:受保护的,无参的show2()");
}
void show3()
{
System.out.println("调用了:默认的,无参的show3()");
}
private String show4(int age)
{
System.out.println("调用了,私有的,并且有返回值的,int参数的show4(): age = " + age); return "abcd";
}
}
测试类
package com.dr.Reflection.getMethodByReflect;import java.lang.reflect.Method;
public class MethodClass {
public static void main(String[] args) throws Exception {
//1.获取Class对象
Class stuClass = Class.forName("com.dr.Reflection.getMethodByReflect.Student");
//2.获取所有公有方法
System.out.println("***************获取所有的”公有“方法*******************");
stuClass.getMethods();
Method[] methodArray = stuClass.getMethods();
for(Method m : methodArray){
/* 注意,这里虽然student类自身只写了一个public方法,但是由于java所有的类都继承于object类,因此object类中所有的public方法也会被打印出来 */
System.out.println(m);
}
System.out.println("***************获取所有的方法,包括私有的*******************");
methodArray = stuClass.getDeclaredMethods();
for(Method m : methodArray)
{
//不包含父类的,仅仅是这个自身定义的方法
System.out.println(m);
}
System.out.println("***************获取公有的show1()方法*******************");
Method m = stuClass.getMethod("show1", String.class);
//根据方法名称,以及参数,获取方法对象
System.out.println(m);
//实例化一个Student对象
Object obj = stuClass.getConstructor().newInstance();
m.invoke(obj, "刘德华");
System.out.println("***************获取私有的show4()方法******************");
m = stuClass.getDeclaredMethod("show4", int.class);
System.out.println(m);
m.setAccessible(true);//解除私有限定
Object result = m.invoke(obj, 20);
//需要两个参数,一个是要调用的对象(获取有反射),一个是实参
System.out.println("返回值:" + result);
}
}