运行时类型信息使得可以在程序运行时发现和实用类型信息
运行时识别对象和类的信息有两种方式
1 RTTI:假设我们在编译时就已经知道了所有的类型
2 反射:允许我们在运行时发现和使用类的信息

为什么需要RTTI

让代码只操纵对基类的使用

  1. abstract class Shape {
  2. void draw(){
  3. System.out.println(this + ".draw()");}//只对基类进行编程
  4. //shape对象实际执行什么样的代码都是由子类决定的
  5. @Override
  6. abstract public String toString();}
  7. class Circle extends Shape{
  8. @Override
  9. public String toString() {
  10. return "Circle";}
  11. }
  12. class Square extends Shape{
  13. @Override
  14. public String toString() {
  15. return "Square";}
  16. }
  17. class Triangle extends Shape{
  18. @Override
  19. public String toString() {
  20. return "Triangle";}
  21. }
  22. public class Shapes {
  23. public static void main(String[] args) {
  24. List<Shape> shapes = Arrays.asList(new Circle(), new Square(), new Triangle());
  25. for (Shape shape : shapes) {
  26. shape.draw();
  27. }
  28. }
  29. }

在java中所有的类型转换都是在运行时进行正确检查的,在运行时识别一个对象的类型

Class对象

Class对象:包含了与类的相关信息
Class对象就是用来创建类的所有常规对象的,java使用Class对象来执行它的RTTI
类是程序的一部分,每个类都有一个Class对象,所有的类都是对其第一次使用时动态的加载到虚拟机中的。
一旦某个类的Class对象被载入内存,他就被用来创建这个类的所有对象

  1. class Candy{
  2. static {System.out.println("Loading Candy");}
  3. }
  4. class Gum{
  5. static {System.out.println("Loading Gum");}
  6. }
  7. class Cookie{
  8. static {System.out.println("Loading Cookie");}
  9. }
  10. public class SweetShop {
  11. public static void main(String[] args) {
  12. System.out.println("Inside main");
  13. new Candy();//执行静态代码块
  14. System.out.println("After creating Candy");
  15. try {
  16. Class.forName("stu.chapter14.Gum");//获取Class对象的引用
  17. //如果类Gum类在还没有加载的时候就加载他,它的static语句就会之心
  18. } catch (ClassNotFoundException e) {
  19. System.out.println("Couldn't find Gum");
  20. }
  21. System.out.println("After Class.forName(Gum)");
  22. new Cookie();
  23. System.out.println("After creating Cookie");
  24. }
  25. }

只要想在运行时获得类型信息,就需要首先获得对恰当的Class对象的引用
可以通过getClass对象获取Class引用

  1. interface HasBatteries{}
  2. interface Waterproof{}
  3. interface Shoots{}
  4. class Toy{
  5. Toy(){}
  6. Toy(int i){}
  7. }
  8. class FancyToy extends Toy implements HasBatteries,Waterproof,Shoots{
  9. FancyToy(){ super(1);}
  10. }
  11. public class ToyTest {
  12. static void printInfo(Class cc){
  13. System.out.println("Class name = " + cc.getName() + "is interface ? [" + cc.isInterface() + "]");
  14. System.out.println("Simple name = " + cc.getSimpleName());
  15. System.out.println("Canonical name = " + cc.getCanonicalName());
  16. }
  17. public static void main(String[] args) {
  18. Class c = null;
  19. try {
  20. c = Class.forName("stu.chapter14.FancyToy");
  21. } catch (ClassNotFoundException e) {
  22. System.out.println("Can't find Fancy");
  23. System.exit(1);//退出
  24. }
  25. printInfo(c);
  26. for (Class anInterface : c.getInterfaces()) {//循环遍历它的所有接口
  27. printInfo(anInterface);
  28. }
  29. Class up = c.getSuperclass();//获取父类引用
  30. Object obj = null;
  31. try {
  32. obj = up.newInstance();//创建实例,并且使用这个方法的类必须由默认构造器(无参)
  33. } catch (InstantiationException e) {
  34. System.out.println("Can't instantiate");
  35. System.exit(1);
  36. } catch (IllegalAccessException e) {
  37. System.out.println("Can't Access");
  38. System.exit(1);
  39. }
  40. System.out.println(obj.getClass());
  41. }
  42. }

类字面常量

引用.class
类字面常量不仅仅可以应用于普通的类,也可以应用于接口,数组,以及基本数据类型
当使用“.class”来创建对Class对象的引用的时候不会自动的初始化该Class对象

泛化的Class引用

Class引用表示的就是他所指向的对象的确切类型,而该对象就是Class类的一个对象
通过泛型语法,可以让编译器强制的进行额外的类型检查

  1. public class GenericClassReferences {
  2. public static void main(String[] args) {
  3. Class intClass = int.class;
  4. Class<Integer> genericIntClass = int.class;
  5. genericIntClass = Integer.class;
  6. intClass = double.class;
  7. //genericIntClass = double.class;//已经是同泛型指定了,所以不能再转换成为其他的类型
  8. }
  9. }

使用newInstance创建实例

  1. class CountedInteger{
  2. private static long counter;
  3. private final long id = counter++;
  4. @Override
  5. public String toString() {
  6. return Long.toString(id);
  7. }
  8. }
  9. public class FilledList<T> {
  10. private Class<T> type;
  11. public FilledList(Class<T> type){this.type = type;}
  12. public List<T> create(int nElements) {
  13. List<T> res = new ArrayList<>();//返回值是一个集合
  14. try {
  15. for (int i = 0; i < nElements; i++) {
  16. res.add(type.newInstance());//创建实例
  17. } }catch (Exception e) {
  18. throw new RuntimeException();
  19. }
  20. return res;
  21. }
  22. public static void main(String[] args) {
  23. FilledList<CountedInteger> countedIntegerFilledList = new FilledList<>(CountedInteger.class);
  24. System.out.println(countedIntegerFilledList.create(15));
  25. }
  26. }

类型转换之前先做检查

instanceof:返回一个布尔值,告诉我们前者是否时后者的一个实例

  1. public class PetCount {
  2. static class PetCounter extends HashMap<String,Integer>{
  3. public void count(String type){
  4. Integer quantity = get(type);
  5. if (quantity == null){
  6. put(type, 1);
  7. }
  8. else {
  9. put(type, quantity + 1);
  10. }
  11. }
  12. }
  13. public static void countPets(PetCreator creator){
  14. PetCounter counter = new PetCounter();
  15. for (Pet pet : creator.creatArray(20)) {
  16. System.out.print(pet.getClass().getSimpleName() + " " );
  17. if (pet instanceof Pet){
  18. counter.count("Pet");
  19. }
  20. if (pet instanceof Dog){
  21. counter.count("Dog");
  22. }
  23. if (pet instanceof Mutt){
  24. counter.count("Mutt");
  25. }
  26. if (pet instanceof Pug){
  27. counter.count("Pug");
  28. }
  29. if (pet instanceof Cat){
  30. counter.count("Cat");
  31. }
  32. if (pet instanceof Manx){
  33. counter.count("EgyptianMau");
  34. }
  35. if (pet instanceof Manx){
  36. counter.count("Manx");
  37. }
  38. if (pet instanceof Manx){
  39. counter.count("Cymric");
  40. }
  41. if (pet instanceof Rodent){
  42. counter.count("Rodent");
  43. }
  44. if (pet instanceof Rat){
  45. counter.count("Rat");
  46. }
  47. if (pet instanceof Mouse){
  48. counter.count("Mouse");
  49. }
  50. if (pet instanceof Hamster){
  51. counter.count("Hamster");
  52. }
  53. }
  54. System.out.println();
  55. System.out.println(counter);
  56. }
  57. public static void main(String[] args) {
  58. countPets(new ForNameCreator());
  59. }
  60. }

使用类字面常量

image.png

  1. class Initable {
  2. static final int staticFinal = 47;
  3. static final int staticFinal2 = ClassInitialization.rand.nextInt(1000);
  4. static{
  5. System.out.println("Initializing Initable");
  6. }
  7. }
  8. class Initable2 {
  9. static int staticNonFinal = 147;
  10. static {
  11. System.out.println("Initializing Initable2");
  12. }
  13. }
  14. class Initable3 {
  15. static int staticNonFinal = 74;
  16. static {
  17. System.out.println("Initializing Initable3");
  18. }
  19. }
  20. public class ClassInitialization {
  21. public static Random rand = new Random(47);
  22. public static void main(String[] args) throws ClassNotFoundException {
  23. final Class initableClass = Initable.class;
  24. System.out.println("After creating Initable ref");
  25. //staticFinal是static final的,是一个编译期常量,它不需要对类进行初始化就可以被读取
  26. System.out.println(Initable.staticFinal);
  27. //staticFinal虽然也是static final的,但是他却需要进行运算才可以获取它的值,所以此时的类需要加载到虚拟机中
  28. System.out.println(Initable.staticFinal2);
  29. //下面的常量都是static但是不是的static final的,所以他们在读取之前都要进行初始化
  30. System.out.println(Initable2.staticNonFinal);
  31. final Class initable3 = Class.forName("thinkinginjava.Initable3");
  32. System.out.println("After creating Initable3 ref");
  33. System.out.println(Initable3.staticNonFinal);
  34. }
  35. }
  1. public class LiteralPetCreator extends PetCreator {
  2. public static final List<Class<? extends Pet>> allType =
  3. Collections.unmodifiableList(Arrays.asList(
  4. Pet.class, Dog.class, Cat.class, Rodent.class, Mutt.class, Pug.class, EgyptianMau.class,
  5. Manx.class, Cymric.class, Mouse.class, Hamster.class));
  6. private static final List<Class<? extends Pet>> types = allType.subList(allType.indexOf(Mutt.class), allType.size());
  7. public List<Class <? extends Pet>> types(){
  8. return types;
  9. }
  10. public static void main(String[] args) {
  11. System.out.println(types);
  12. }
  13. }

动态的instanceof

  1. public class PetCount3 {
  2. static class PetCount extends LinkedHashMap<Class<? extends Pet>,Integer>{
  3. public PetCount(){
  4. super(MapData.map(LiteralPetCreator.allType,0));
  5. }
  6. public void count(Pet pet){
  7. for (Map.Entry<Class<? extends Pet>, Integer> pair : entrySet()) {
  8. if (pair.getKey().isInstance(pet)){
  9. put(pair.getKey(), pair.getValue() + 1);
  10. }
  11. }
  12. }
  13. public String toString(){
  14. final StringBuilder result = new StringBuilder("{");
  15. for (Map.Entry<Class<? extends Pet>, Integer> pair : entrySet()) {
  16. result.append(pair.getKey().getSimpleName());
  17. result.append("=");
  18. result.append(pair.getValue());
  19. result.append(", ");
  20. }
  21. result.delete(result.length() - 2, result.length());
  22. result.append("}");
  23. return result.toString();
  24. }
  25. }
  26. public static void main(String[] args) {
  27. final PetCount petCount = new PetCount();
  28. for (Pet pet : Pets.creatArray(20)) {
  29. System.out.print(pet.getClass().getSimpleName() + " ");
  30. petCount.count(pet);
  31. }
  32. System.out.println();
  33. System.out.println(petCount);
  34. }
  35. }

注册工厂

将对象的创建工作交给自己去完成,工厂方法可以多态的被调用

  1. public interface Factory<T> {
  2. T creat();
  3. }
  4. class Part{
  5. @Override
  6. public String toString() {
  7. return getClass().getSimpleName();
  8. }
  9. static List<Factory<? extends Part>> partFactories = new ArrayList<>();
  10. static {
  11. partFactories.add(new FanBelt.Factory());
  12. partFactories.add(new FuelFilter.Factory());
  13. }
  14. private static Random rand = new Random(47);
  15. static Part createRandom(){
  16. int n = rand.nextInt(partFactories.size());//随机的创建实例
  17. return partFactories.get(n).creat();
  18. }
  19. }
  20. class Filter extends Part{} //分类标识,创建的是他们的子类
  21. class FuelFilter extends Filter{
  22. public static class Factory implements stu.chapter14.Factory<FuelFilter>{
  23. @Override
  24. public FuelFilter creat() {
  25. return new FuelFilter();
  26. }
  27. }
  28. }
  29. class Belt extends Part{}
  30. class FanBelt extends Belt{
  31. public static class Factory implements stu.chapter14.Factory<FanBelt>{
  32. @Override
  33. public FanBelt creat() {
  34. return new FanBelt();
  35. }
  36. }
  37. }
  38. public class RegisteredFactories {
  39. public static void main(String[] args) {
  40. final Part part = new Part();
  41. for (int i = 0; i < 10; i++) {
  42. System.out.println(Part.createRandom());
  43. }
  44. }
  45. }

instanceof和Class的等价性

在查询类型信息的时候,instanceof或者isInstance()和直接比较Class对象由一个很重要的差别

  1. class Base{}
  2. class Derived extends Base{}
  3. public class FamilyVsExactType {
  4. static void test(Object x){
  5. System.out.println("Testing x of type: " + x.getClass());
  6. System.out.println("x instanceof Base: " + (x instanceof Base));
  7. System.out.println("x instanceof Derived: " + (x instanceof Derived));
  8. System.out.println("Base.class.isInstance(x): " + Base.class.isInstance(x));
  9. System.out.println("Derived.class.isInstance(x): " + Derived.class.isInstance(x));
  10. System.out.println("x.getClass() == Base.class: " + (x.getClass() == Base.class));
  11. System.out.println("x.getClass() == Derived.class: " + (x.getClass() == Derived.class));
  12. System.out.println("x.getClass().equals(Base.class): " + (x.getClass().equals(Base.class)));
  13. System.out.println("x.getClass().equals(Derived.class): " + (x.getClass().equals(Derived.class)));
  14. }
  15. public static void main(String[] args) {
  16. test(new Base());
  17. test(new Derived());
  18. }
  19. }

instanceof或者isInstance()表示的是你是这个类吗?或者说是它的子类吗
==和equals表示的是实际的对象,没有继承关系

反射:运行时的类型信息

对于反射机制而言.class文件在编译时时不可取的,所以时在运行时打开和检查.class文件

类方法提取器

可以动态的提取某个类的信息
静态代理:

  1. interface Interface{
  2. void doSomething();
  3. void somethingElse(String arg);
  4. }
  5. class RealObject implements Interface{
  6. @Override
  7. public void doSomething() {
  8. System.out.println("doSomething");
  9. }
  10. @Override
  11. public void somethingElse(String arg) {
  12. System.out.println("doSomethingElse" + arg);
  13. }
  14. }
  15. class SimpleProxy implements Interface{
  16. private Interface proxied;
  17. public SimpleProxy(Interface proxied){
  18. this.proxied = proxied; //调用父类的方法
  19. }
  20. @Override
  21. public void doSomething() {
  22. System.out.println("SimpleProxy doSomething");
  23. proxied.doSomething();//调用父类的方法
  24. }
  25. @Override
  26. public void somethingElse(String arg) {
  27. System.out.println("SimpleProxy somethingElse" + arg);
  28. proxied.somethingElse(arg);
  29. }
  30. }
  31. public class SimpleProxyDemo {
  32. public static void consumer(Interface iFace){
  33. iFace.doSomething();
  34. iFace.somethingElse("bonobo");
  35. }
  36. public static void main(String[] args) {
  37. consumer(new RealObject());
  38. consumer(new SimpleProxy(new RealObject()));
  39. //SimpleProxy中的构造器要传入Interface类型
  40. }
  41. }

动态代理:动态的创建代理,并且动态的处理对代理方法的调用。在动态代理上所作的所有调用都会被重定向到单一的调用处理器上(InvacationHandler)上。

  1. class DynamicProxyHandler implements InvocationHandler{ //实现InvocationHandler
  2. private Object proxied;
  3. public DynamicProxyHandler(Object proxied){
  4. this.proxied = proxied;
  5. }
  6. @Override
  7. public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
  8. System.out.println("proxy: " + proxy.getClass() + ", method: " + method + ", args: " + args);
  9. /*if (args != null){
  10. for (Object arg : args) {
  11. System.out.println(" " + arg);
  12. }
  13. }*/
  14. return method.invoke(proxied, args);//通过这个方法来调用外部的方法
  15. }
  16. }
  17. public class SimpleDynamicProxy {
  18. public static void consumer(Interface iFace){
  19. //iFace.doSomething();
  20. iFace.somethingElse("bonobo");
  21. }
  22. public static void main(String[] args) {
  23. RealObject real = new RealObject();
  24. //consumer(real);
  25. //类加载器
  26. final Object o = Proxy.newProxyInstance(Interface.class.getClassLoader(),
  27. new Class[]{Interface.class}, new DynamicProxyHandler(real));//最后一个就是调用处理器
  28. //必须实现InvocationHandler
  29. //通过它传递一个实际对象的引用
  30. //final Object o = Proxy.newProxyInstance(Interface.class.getClassLoader(),
  31. //RealObject.class.getInterfaces(),//需要干活的类的所有接口
  32. //new DynamicProxyHandler(real));
  33. consumer((Interface) o);
  34. }
  35. }

还可以筛选某些方法

  1. interface SomeMethods{
  2. void boring1();
  3. void boring2();
  4. void interesting(String arg);
  5. void boring3();
  6. }
  7. class Implementation implements SomeMethods{
  8. @Override
  9. public void boring1() {
  10. System.out.println("boring1()");
  11. }
  12. @Override
  13. public void boring2() {
  14. System.out.println("boring2()");
  15. }
  16. @Override
  17. public void interesting(String arg) {
  18. System.out.println("interesting " + arg);
  19. }
  20. @Override
  21. public void boring3() {
  22. System.out.println("boring3");
  23. }
  24. }
  25. class MethodSelector implements InvocationHandler{
  26. private Object proxied;
  27. public MethodSelector(Object proxied){
  28. this.proxied = proxied;
  29. }
  30. @Override
  31. public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
  32. if (method.getName().equals("interesting"))
  33. System.out.println("Proxy detected the interesting");
  34. return method.invoke(proxied,args);
  35. }
  36. }
  37. public class SelectingMethods {
  38. public static void main(String[] args) {
  39. SomeMethods proxy = (SomeMethods) Proxy.newProxyInstance(SomeMethods.class.getClassLoader(),
  40. Implementation.class.getInterfaces(), new MethodSelector(new Implementation()));
  41. //proxy.boring1();
  42. //proxy.boring2();
  43. proxy.interesting("ahah");
  44. //proxy.boring3();
  45. }
  46. }

接口和类型信息

  1. public interface A {
  2. void f();
  3. }
  4. class B implements A{
  5. @Override
  6. public void f(){}
  7. public void g(){}
  8. }
  9. public class InterfaceViolation {
  10. public static void main(String[] args) {
  11. A a = new B();
  12. a.f();
  13. System.out.println(a.getClass().getName());
  14. // a这个实例中看不到g方法
  15. B b = (B) a;
  16. b.f();
  17. b.g();
  18. //通过转型可以调用A中不存在的方法
  19. }
  20. }

可以借助访问权限对其进行限制

  1. class C implements A{
  2. @Override
  3. public void f() {
  4. System.out.println("public.C.f()");
  5. }
  6. public void g(){
  7. System.out.println("public.C.g()");
  8. }
  9. void u(){
  10. System.out.println("Package.C.u()");
  11. }
  12. protected void v(){
  13. System.out.println("protect.C.v()");
  14. }
  15. private void w(){
  16. System.out.println("private.C.w()");
  17. }
  18. }
  19. public class HiddenC {
  20. public static A makeA(){
  21. return new C();
  22. }
  23. public static void main(String[] args) {
  24. C c = (C) HiddenC.makeA();
  25. c.v();
  26. }
  27. }

但是通过反射可以拿到任何的域或者方法,甚至时私有的

  1. public class HiddenImplementation {
  2. static void callHiddenMethods(Object a ,String methodName) throws Exception {
  3. Method g = a.getClass().getDeclaredMethod(methodName);
  4. g.setAccessible(true);
  5. g.invoke(a);
  6. }
  7. public static void main(String[] args) throws Exception {
  8. A a = HiddenC.makeA();
  9. System.out.println(a.getClass().getName());
  10. a.f();
  11. /*if (a instanceof C){
  12. C c = (C)a;
  13. c.g();
  14. }*/
  15. callHiddenMethods(a, "g");
  16. callHiddenMethods(a, "u");
  17. callHiddenMethods(a, "v");
  18. callHiddenMethods(a, "w");
  19. }
  20. }

此时只能通过final修饰,使其只读但是不可以更改

  1. class WithPrivateFinalField{
  2. private int i = 1;
  3. private final String s = "I'm totally safe";
  4. private String s2 = "Am I safe?";
  5. @Override
  6. public String toString() {
  7. return "WithPrivateFinalField{" +
  8. "i=" + i +
  9. ", s='" + s + '\'' +
  10. ", s2='" + s2 + '\'' +
  11. '}';
  12. }
  13. }
  14. public class ModifyingPrivateFields {
  15. public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException {
  16. WithPrivateFinalField pf = new WithPrivateFinalField();
  17. System.out.println(pf);
  18. Field f = pf.getClass().getDeclaredField("i");
  19. f.setAccessible(true);
  20. System.out.println("f.getInt(pf) : " + f.getInt(pf));
  21. f.setInt(pf,47);
  22. System.out.println(pf);
  23. f = pf.getClass().getDeclaredField("s");
  24. f.setAccessible(true);
  25. System.out.println("f.get(pf) : " + f.get(pf));
  26. f.set(pf,"haha");
  27. System.out.println(pf);
  28. f = pf.getClass().getDeclaredField("s2");
  29. f.setAccessible(true);
  30. System.out.println("f.get(pf) : " + f.get(pf));
  31. f.set(pf,"heihei");
  32. System.out.println(pf);
  33. }
  34. }