1. Java中的多态性理解(注意与C++区分)

  • Java中除了static方法和final方法(private方法本质上属于final方法,因为不能被子类访问)之外,其它所有的方法都是动态绑定,这意味着通常情况下,我们不必判定是否应该进行动态绑定—它会自动发生。

    • final方法会使编译器生成更有效的代码,这也是为什么说声明为final方法能在一定程度上提高性能(效果不明显)。
    • 如果某个方法是静态的,它的行为就不具有多态性:
      class StaticSuper {public static String staticGet() { return "Base staticGet()"; } public String dynamicGet() { return "Base dynamicGet()"; } } class StaticSub extends StaticSuper { public static String staticGet() { return "Derived staticGet()"; } public String dynamicGet() { return "Derived dynamicGet()"; } } public class StaticPolymorphism { public static void main(String[] args) { StaticSuper sup = new StaticSub(); System.out.println(sup.staticGet()); System.out.println(sup.dynamicGet()); } } 

      输出:

      Base staticGet()
      Derived dynamicGet()

  • 构造函数并不具有多态性,它们实际上是static方法,只不过该static声明是隐式的。因此,构造函数不能够被override。

  • 在父类构造函数内部调用具有多态行为的函数将导致无法预测的结果,因为此时子类对象还没初始化,此时调用子类方法不会得到我们想要的结果。

    class Glyph {void draw() { System.out.println("Glyph.draw()"); } Glyph() { System.out.println("Glyph() before draw()"); draw(); System.out.println("Glyph() after draw()"); } } class RoundGlyph extends Glyph { private int radius = 1; RoundGlyph(int r) { radius = r; System.out.println("RoundGlyph.RoundGlyph(). radius = " + radius); } void draw() { System.out.println("RoundGlyph.draw(). radius = " + radius); } } public class PolyConstructors { public static void main(String[] args) { new RoundGlyph(5); } } 

    输出:

    Glyph() before draw()
    RoundGlyph.draw(). radius = 0
    Glyph() after draw()
    RoundGlyph.RoundGlyph(). radius = 5

为什么会这样输出?这就要明确掌握Java中构造函数的调用顺序

(1)在其他任何事物发生之前,将分配给对象的存储空间初始化成二进制0;
(2)调用基类构造函数。从根开始递归下去,因为多态性此时调用子类覆盖后的draw()方法(要在调用RoundGlyph构造函数之前调用),由于步骤1的缘故,我们此时会发现radius的值为0;
(3)按声明顺序调用成员的初始化方法;
(4)最后调用子类的构造函数。

  • 只有非private方法才可以被覆盖,但是还需要密切注意覆盖private方法的现象,这时虽然编译器不会报错,但是也不会按照我们所期望的来执行,即覆盖private方法对子类来说是一个新的方法而非重载方法。因此,在子类中,新方法名最好不要与基类的private方法采取同一名字(虽然没关系,但容易误解,以为能够覆盖基类的private方法)

  • Java类中属性域的访问操作都由编译器解析,因此不是多态的。父类和子类的同名属性都会分配不同的存储空间,如下:

    // Direct field access is determined at compile time.
    class Super {public int field = 0; public int getField() { return field; } } class Sub extends Super { public int field = 1; public int getField() { return field; } public int getSuperField() { return super.field; } } public class FieldAccess { public static void main(String[] args) { Super sup = new Sub(); System.out.println("sup.filed = " + sup.field + ", sup.getField() = " + sup.getField()); Sub sub = new Sub(); System.out.println("sub.filed = " + sub.field + ", sub.getField() = " + sub.getField() + ", sub.getSuperField() = " + sub.getSuperField()); } } 

    输出:

    sup.filed = 0, sup.getField() = 1
    sub.filed = 1, sub.getField() = 1, sub.getSuperField() = 0

    Sub子类实际上包含了两个称为field的域,然而在引用Sub中的field时所产生的默认域并非Super版本的field域,因此为了得到Super.field,必须显式地指明super.field。

2. is-a关系和is-like-a关系

  • is-a关系属于纯继承,即只有在基类中已经建立的方法才可以在子类中被覆盖,如下图所示:

    基类和子类有着完全相同的接口,这样向上转型时永远不需要知道正在处理的对象的确切类型,这通过多态来实现。

  • is-like-a关系:子类扩展了基类接口。它有着相同的基本接口,但是他还具有由额外方法实现的其他特性。

    缺点就是子类中接口的扩展部分不能被基类访问,因此一旦向上转型,就不能调用那些新方法。

3. 运行时类型信息(RTTI + 反射)

  • 概念
    RTTI:运行时类型信息使得你可以在程序运行时发现和使用类型信息。
  • 使用方式
    Java是如何让我们在运行时识别对象和类的信息的,主要有两种方式(还有辅助的第三种方式,见下描述):

    • 一种是“传统的”RTTI,它假定我们在编译时已经知道了所有的类型,比如Shape s = (Shape)s1;
    • 另一种是“反射”机制,它运行我们在运行时发现和使用类的信息,即使用Class.forName()
    • 其实还有第三种形式,就是关键字instanceof,它返回一个bool值,它保持了类型的概念,它指的是“你是这个类吗?或者你是这个类的派生类吗?”。而如果用==或equals比较实际的Class对象,就没有考虑继承—它或者是这个确切的类型,或者不是。
  • 工作原理
    要理解RTTI在Java中的工作原理,首先必须知道类型信息在运行时是如何表示的,这项工作是由称为Class对象的特殊对象完成的,它包含了与类有关的信息。Java送Class对象来执行其RTTI,使用类加载器的子系统实现

无论何时,只要你想在运行时使用类型信息,就必须首先获得对恰当的Class对象的引用,获取方式有三种:
(1)如果你没有持有该类型的对象,则Class.forName()就是实现此功能的便捷途,因为它不需要对象信息;
(2)如果你已经拥有了一个感兴趣的类型的对象,那就可以通过调用getClass()方法来获取Class引用了,它将返回表示该对象的实际类型的Class引用。Class包含很有有用的方法,比如:

package rtti;
interface HasBatteries{} interface WaterProof{} interface Shoots{} class Toy { Toy() {} Toy(int i) {} } class FancyToy extends Toy implements HasBatteries, WaterProof, Shoots { FancyToy() { super(1); } } public class RTTITest { static void printInfo(Class cc) { System.out.println("Class name: " + cc.getName() + ", is interface? [" + cc.isInterface() + "]"); System.out.println("Simple name: " + cc.getSimpleName()); System.out.println("Canonical name: " + cc.getCanonicalName()); } public static void main(String[] args) { Class c = null; try { c = Class.forName("rtti.FancyToy"); // 必须是全限定名(包名+类名) } catch(ClassNotFoundException e) { System.out.println("Can't find FancyToy"); System.exit(1); } printInfo(c); for(Class face : c.getInterfaces()) { printInfo(face); } Class up = c.getSuperclass(); Object obj = null; try { // Requires default constructor. obj = up.newInstance(); } catch (InstantiationException e) { System.out.println("Can't Instantiate"); System.exit(1); } catch (IllegalAccessException e) { System.out.println("Can't access"); System.exit(1); } printInfo(obj.getClass()); } } 

输出:

Class name: rtti.FancyToy, is interface? [false]
Simple name: FancyToy
Canonical name: rtti.FancyToy
Class name: rtti.HasBatteries, is interface? [true]
Simple name: HasBatteries
Canonical name: rtti.HasBatteries
Class name: rtti.WaterProof, is interface? [true]
Simple name: WaterProof
Canonical name: rtti.WaterProof
Class name: rtti.Shoots, is interface? [true]
Simple name: Shoots
Canonical name: rtti.Shoots
Class name: rtti.Toy, is interface? [false]
Simple name: Toy
Canonical name: rtti.Toy

(3)Java还提供了另一种方法来生成对Class对象的引用,即使用类字面常量。比如上面的就像这样:FancyToy.class;来引用。
这样做不仅更简单,而且更安全,因为它在编译时就会受到检查(因此不需要置于try语句块中),并且它根除了对forName方法的引用,所以也更高效。类字面常量不仅可以应用于普通的类,也可以应用于接口、数组以及基本数据类型。


注意:当使用“.class”来创建对Class对象的引用时,不会自动地初始化该Class对象,初始化被延迟到了对静态方法(构造器隐式的是静态的)或者非final静态域(注意final静态域不会触发初始化操作)进行首次引用时才执行:。而使用Class.forName时会自动的初始化。

为了使用类而做的准备工作实际包含三个步骤:
- 加载:由类加载器执行。查找字节码,并从这些字节码中创建一个Class对象
- 链接:验证类中的字节码,为静态域分配存储空间,并且如果必需的话,将解析这个类创建的对其他类的所有引用。
- 初始化:如果该类具有超类,则对其初始化,执行静态初始化器和静态初始化块。

这一点非常重要,下面通过一个实例来说明这两者的区别:

package rtti;
import java.util.Random; class Initable { static final int staticFinal = 47; static final int staticFinal2 = ClassInitialization.rand.nextInt(1000); static { System.out.println("Initializing Initable"); } } class Initable2 { static int staticNonFinal = 147; static { System.out.println("Initializing Initable2"); } } class Initable3 { static int staticNonFinal = 74; static { System.out.println("Initializing Initable3"); } } public class ClassInitialization { public static Random rand = new Random(47); public static void main(String[] args) { // Does not trigger initialization Class initable = Initable.class; System.out.println("After creating Initable ref"); // Does not trigger initialization System.out.println(Initable.staticFinal); // Does trigger initialization(rand() is static method) System.out.println(Initable.staticFinal2); // Does trigger initialization(not final) System.out.println(Initable2.staticNonFinal); try { Class initable3 = Class.forName("rtti.Initable3"); } catch (ClassNotFoundException e) { System.out.println("Can't find Initable3"); System.exit(1); } System.out.println("After creating Initable3 ref"); System.out.println(Initable3.staticNonFinal); } } 

输出:

After creating Initable ref
47
Initializing Initable
258
Initializing Initable2
147
Initializing Initable3
After creating Initable3 ref
74


  • RTTI的限制?如何突破? — 反射机制
    如果不知道某个对象的确切类型,RTTI可以告诉你,但是有一个限制:这个类型在编译时必须已知,这样才能使用RTTI识别它,也就是在编译时,编译器必须知道所有要通过RTTI来处理的类。

可以突破这个限制吗?是的,突破它的就是反射机制
Class类与java.lang.reflect类库一起对反射的概念进行了支持,该类库包含了FieldMethod以及Constructor类(每个类都实现了Member接口)。这些类型的对象是由JVM在运行时创建的,用以表示未知类里对应的成员。这样你就可以使用Constructor创建新的对象,用get()/set()方法读取和修改与Field对象关联的字段,用invoke()方法调用与Method对象关联的方法。另外,还可以调用getFields()、getMethods()和getConstructors()等很便利的方法,以返回表示字段、方法以及构造器的对象的数组。这样,匿名对象的类信息就能在运行时被完全确定下来,而在编译时不需要知道任何事情。


####反射与RTTI的区别
当通过反射与一个未知类型的对象打交道时,JVM只是简单地检查这个对象,看它属于哪个特定的类(就像RTTI那样),在用它做其他事情之前必须先加载那个类的Class对象,因此,那个类的.class文件对于JVM来说必须是可获取的:要么在本地机器上,要么可以通过网络取得。所以RTTI与反射之间真正的区别只在于:对RTTI来说,编译器在编译时打开和检查.class文件(也就是可以用普通方法调用对象的所有方法);而对于反射机制来说,.class文件在编译时是不可获取的,所以是在运行时打开和检查.class文件。

下面的例子是用反射机制打印出一个类的所有方法(包括在基类中定义的方法):

package typeinfo;import java.lang.reflect.Constructor; import java.lang.reflect.Method; import java.util.regex.Pattern; // Using reflection to show all the methods of a class. // even if the methods are defined in the base class. public class ShowMethods { private static String usage = "usage: \n" + "ShowMethods qualified.class.name\n" + "To show all methods in class or: \n" + "ShowMethods qualified.class.name word\n" + "To search for methods involving 'word'"; private static Pattern p = Pattern.compile("\\w+\\."); public static void main(String[] args) { if(args.length < 1) { System.out.println(usage); System.exit(0); } int lines = 0; try { Class<?> c = Class.forName(args[0]); Method[] methods = c.getMethods(); Constructor[] ctors = c.getConstructors(); if(args.length == 1) { for(Method method : methods) { System.out.println(p.matcher(method.toString()).replaceAll("")); } for(Constructor ctor : ctors) { System.out.println(p.matcher(ctor.toString()).replaceAll("")); } lines = methods.length + ctors.length; } else { for(Method method : methods) { if(method.toString().indexOf(

转载于:https://www.cnblogs.com/lucky-star-star/p/5415698.html

Java编程思想非主流知识点相关推荐

  1. JAVA编程思想学习笔记——第一章 对象导论

    搞了一年多java,野路子出身,发现java基础这块还是相当的薄弱!故决定学习<Java编程思想>这本书.在此把学习的知识点记录下! 面向对象的五大特性 1.万物皆为对象 2.程序是对象的 ...

  2. 思维导图学Java编程思想

    用了1个月时间,把<Java编程思想>读了一遍.然后用Mindjet Mindmanager做了读书笔记,跟大家分享一下~ 这个思维导图的读书笔记没有涵盖书中的所有知识点,但是作为框架和读 ...

  3. 《Java编程思想》读书笔记

    前言:三年之前就买了<Java编程思想>这本书,但是到现在为止都还没有好好看过这本书,这次希望能够坚持通读完整本书并整理好自己的读书笔记,上一篇文章是记录的第十七章到第十八章的内容,这一次 ...

  4. 《Java编程思想》读书笔记一

    很早之前就买了<Java编程思想>这本书,初学时看这本书看的云里雾里的,实在费劲,就放在一边垫桌底了.感觉这本书是适合C/C++程序员转行到Java学习的一本书,并不适合零基础的初学者去看 ...

  5. 【Java编程思想】读书笔记(一)第一章---第五章

    Java编程思想(第四版)学习笔记 第一章---第五章 第一章:对象导论 1.1抽象过程 1. 2访问控制 第二章:一切都是对象 2. 1用引用操纵对象 2. 2基本类型 第三章:操作符 3.7.1测 ...

  6. 厉害了 全靠经典之作-Java编程思想,把你教的明明白白

    今天我们来聊聊这本<Java编程思想> 从我学习Java的经验来看,<Thinking in Java>是讲解Java编程的最佳书籍! 这本书不仅详细地介绍Java语法.知识点 ...

  7. 《Java编程思想》读后总结(一)

    前言 <Java编程思想>这本书,陆陆续续读了1年,终于基本都浏览了一遍.通过这本书,试图理解作者的想法,才真的体会到Java思想.感谢本书的作者,不仅讲述了java的语法,更重要的是向读 ...

  8. Java编程思想个人读后感

    今天又拿起了Java编程思想啃了起来,前段时间看了二十来章,但感觉心静不下来,有许多知识没有消化,所以昨天又从第一章开始学起,这次也不强加自己一天要学一两章,也觉得那样没什么效果,只有静下心来,把知识 ...

  9. 《Java编程思想》读书笔记(二)

    三年之前就买了<Java编程思想>这本书,但是到现在为止都还没有好好看过这本书,这次希望能够坚持通读完整本书并整理好自己的读书笔记,上一篇文章是记录的第一章到第十章的内容,这一次记录的是第 ...

最新文章

  1. LeetCode中等题之在排序数组中查找元素的第一个和最后一个位置
  2. PYTHON学习笔记-DAY-16
  3. Android开发实践:以“专业”的态度处理多线程
  4. python图像边缘检测
  5. Linux运维工程师面试题第二套
  6. jvm四:常量的本质含义以及助记符基本认识
  7. 数据中心智慧机房解决方案
  8. Idea新建项目默认是JDK1.5解决办法
  9. java散列法的运用实例,Java HashMap compute() 使用方法及示例
  10. day15 webUI自动化
  11. SpringCloud工作笔记074---Idea2018 1.6发现不支持@Slf4j,@Getter ,@Setter注解
  12. docker强制删除none的image镜像
  13. c语言二维数组错误语法,关于c语言动态分配二维数组free的错误求dalao看看怎么回事谢谢啊~~~~...
  14. Android 控件系列篇
  15. Xshell设置密钥登录CentOS6.5_64位(图文版)
  16. 常用的第三方ui框架
  17. 服务器如何防止DDoS攻击?
  18. TCP/IP协议族的数据链路层基础(1)——MTU
  19. 阿里云——云开发平台基于Python的web项目部署到Serverless
  20. 企业邮箱申请流程有哪些?公司邮箱的优势有哪些?

热门文章

  1. Java版的jQuery
  2. 在 命令行 (cmd)执行 Maven命令,对java工程进行打包 操作 (指定settings.xml)
  3. 2020-11-19实习
  4. android tts 语音合成
  5. IT忍者神龟之Html规范
  6. 【达内课程】Application
  7. 2.5 黑群晖驱动:正确显示CPU型号和获取CPU真实温度 教程
  8. Hidemyass代理审查 - 审查的隐藏我的屁股代理服务
  9. 【渝粤题库】陕西师范大学292061 会计学基础 作业 (高起专)
  10. 简单实用的腿部按摩工具,HEAD海德环形夹腿器