有3个概念:
编译时
运行时
构建时
理解这3个概念可以很好的帮助我们去理解一些基本的概念。
- 方法重载 -> 编译期,编译时多态,根据参数类型,决定生成调用哪个方法的字节码
- 方法覆盖 -> 运行期, 运行时多态, 根据对象的类型, 决定调用哪个实例方法
- 继承 -> 编译期,因为是静态的。
-
泛型(又称类型检验)-> 发生在编译期的。编译器负责检查程序中类型的正确性,然后把使用了泛型的代码翻译或者重写成可以执行在当前JVM上的非泛型代码。
- 注解Annotation -> 编译时注解@Override,可以用来捕获类似于在子类中把toString()写成tostring()这样的错误;Junit的@Test运行时注解
- 异常Exception -> 编译时/运行时。运行时异常不需要编译器来检测
- AOP切面
- Spring的IOC,
- Class.forname()
- 组合优于继承
- BeanFactory根据xml文件,结合Class.forname
代理或组合->运行时, "设计模式 -> 组合优于继承"
继承是一种多态工具,而不是一种代码复用工具。是否使用继承的规则:是继承只能用在类之间有“父子”关系的情况下。
- 不要仅仅为了代码复用而继承。过度使用继承(通过“extends”关键字)的话,如果修改了父类,会损坏所有的子类。这是因为子类和父类的紧耦合关系是在编译期产生的。
- 不要仅仅为了多态而继承。如果你的类之间没有继承关系,并且你想要实现多态,那么你可以通过接口和组合的方式来实现,这样不仅可以实现代码重用,同时也可以实现运行时的灵活性。
面试者会在你的答案里着重关注这几个词语——“耦合”,“静态还是动态”,以及“发生在编译期还是运行时”。运行时的灵活性可以通过组合来实现,因为类可以在运行时动态地根据一个结果有条件或者无条件地进行组合。但是继承却是静态的。
Java语言本身不支持运行时继承,但是有一种替代的方案叫做“代理”或者“组合”,它表示在运行时组件一个层次对象的子类。这样可以模拟运行时继承的实现。
-------------------------------
Class.forName("xx.xx")等同于
Class.forName("xx.xx",true,ClassLoader.getSystemClassLoader());//此时已经初始化实例对象了
而ClassLoader loader = ClassLoader.getSystemClassLoader();
Class className=loader.loadClass("xx.xx");//此时class没有实例化对象
className.newInstance();//此时才真正的初始化实例对象
综上所述它们的区别在于
- Class.forName("xx.xx")已经实例化类对象了
- ClassLoader.loadClass("xx.xx");没有实例化类对象,需要调用newInstance方法进行实例化
----------------------------------
看下面A行和B行代码的区别
public class ConstantFolding { static final int number1 = 5; static final int number2 = 6; static int number3 = 5; static int number4= 6; public static void main(String[ ] args) { int product1 = number1 * number2; //line A int product2 = number3 * number4; //line B } }
在行A的代码中,product的值是在编译期计算的,行B则是在运行时计算的。如果你使用Java反编译器(例如,jd-gui)来反编译ConstantFolding.class文件的话,那么你就会从下面的结果里得到答案。
public class ConstantFolding { static final int number1 = 5; static final int number2 = 6; static int number3 = 5; static int number4 = 6; public static void main(String[ ] args) { int product1 = 30; int product2 = number3 * number4; } }
常量折叠是一种Java编译器使用的优化技术。
由于final变量的值不会改变,因此就可以对它们优化。
Java反编译器和javap命令都是查看编译后的代码(例如,字节码)的利器。
---------------------
1. 方法重载
这个是发生在编译时的。方法重载也被称为编译时多态,因为编译器可以根据参数的类型来选择使用哪个方法。
public class { public void evaluate(A a); // method #1 public void evaluate(B b); // method #2 public void evaluate(C c); //method 3, C extends A }
如果编译器要编译下面的语句的话:
C c = new C(); evaluate(c);
会根据传入的参数类型,调用method3,如果没有method3,则向上转型调用method1
注意:静态方法可以被继承,但不能被覆盖
B继承A A有静态方法test() B也有静态方法test() 测试: A a = new B(); a.test();//调用的是A的静态方法
------------------
2.方法覆盖:
运行时发生的。方法重载被称为运行时多态,因为在编译期编译器不知道并且没法知道该去调用哪个方法。JVM会在代码运行的时候做出决定。
public class A { public int compute(int input) { //method #3 return 3 * input; } } public class B extends A { @Override public int compute(int input) { //method #4 return 4 * input; } }
子类B中的compute(..)方法重写了父类的compute(..)方法。如果编译器遇到下面的代码:
public int evaluate(A reference, int arg2) { int result = reference.compute(arg2); }
编译器是没法知道传入的参数reference的类型是A还是B。
因此,只能够在运行时,根据赋给输入变量“reference”的对象的类型(例如,A或者B的实例)来决定调用方法#3还是方法#4.
-------------------
3.泛型(又称类型检验):
这个是发生在编译期的。
编译器负责检查程序中类型的正确性,然后把使用了泛型的代码翻译或者重写成可以执行在当前JVM上的非泛型代码。这个技术被称为“类型擦除“。换句话来说,编译器会擦除所有在尖括号里的类型信息,来保证和版本1.4.0或者更早版本的JRE的兼容性。
List<String> myList = new ArrayList<String>(10); 编译后 List myList = new ArrayList(10);
-------------------
4.注解(Annotation):
你可以使用运行时或者编译时的注解。
public class B extends A { @Override public int compute(int input){ //method #4 return 4 * input; } }
@Override是一个简单的编译时注解,
它可以用来捕获类似于在子类中把toString()写成tostring()这样的错误。
在Java 5中,用户自定义的注解可以用注解处理工具(Anotation Process Tool ——APT)在编译时进行处理。到了Java 6,这个功能已经是编译器的一部分了
public class MyTest{ @Test public void testEmptyness( ){ org.junit.Assert.assertTrue(getList( ).isEmpty( )); } private List getList( ){ //implemenation goes here } }
@Test是JUnit框架用来在运行时通过反射来决定调用测试类的哪个(些)方法的注解。
@Test (timeout=100) public void testTimeout( ) { while(true); //infinite loop } //如果运行时间超过100ms的话,上面的测试用例就会失败。 @Test (expected=IndexOutOfBoundsException.class) public void testOutOfBounds( ) { new ArrayList<Object>( ).get(1); } //如果上面的代码在运行时没有抛出IndexOutOfBoundsException或者抛出的是其他的异常的话,那么这个用例就会失败。用户自定义的注解可以在运行时通过Java反射API里新增的AnnotatedElement和”Annotation”元素接口来处理。
5.异常(Exception)
你可以使用运行时异常或者编译时异常。
运行时异常(RuntimeException)也称作未检测的异常(unchecked exception),这表示这种异常不需要编译器来检测。RuntimeException是所有可以在运行时抛出的异常的父类。一个方法除要捕获异常外,如果它执行的时候可能会抛出RuntimeException的子类,那么它就不需要用throw语句来声明抛出的异常。
例如:NullPointerException,ArrayIndexOutOfBoundsException,等等
受检查异常(checked exception)都是编译器在编译时进行校验的,通过throws语句或者try{}cathch{} 语句块来处理检测异常。编译器会分析哪些异常会在执行一个方法或者构造函数的时候抛出。
6.面向切面的编程(Aspect Oriented Programming-AOP):
切面可以在编译时,运行时或,加载时或者运行时织入。
编译期:编译期织入是最简单的方式。如果你拥有应用的代码,你可以使用AOP编译器(例如,ajc – AspectJ编译器)对源码进行编译,然后输出织入完成的class文件。AOP编译的过程包含了waver的调用。切面的形式可以是源码的形式也可以是二进制的形式。如果切面需要针对受影响的类进行编译,那么你就需要在编译期织入了。
编译后:这种方式有时候也被称为二进制织入,它被用来织入已有的class文件和jar文件。和编译时织入方式相同,用来织入的切面可以是源码也可以是二进制的形式,并且它们自己也可以被织入切面。
装载期:这种织入是一种二进制织入,它被延迟到JVM加载class文件和定义类的时候。为了支持这种织入方式,需要显式地由运行时环境或者通过一种“织入代理(weaving agent)“来提供一个或者多个“织入类加载器(weaving class loader)”。
运行时:对已经加载到JVM里的类进行织入
--------------------------------------------------------
7.继承 – 发生在编译时,因为它是静态的
代理或者组合 – 发生在运行时,因为它更加具有动态性和灵活性。
Q.你有没有听说过“组合优于继承”这样的说法呢?如果听说过的话,那么你是怎么理解的呢?
A.继承是一种多态工具,而不是一种代码复用工具。有些开发者喜欢用继承的方式来实现代码复用,即使是在没有多态关系的情况下。是否使用继承的规则是继承只能用在类之间有“父子”关系的情况下。
不要仅仅为了代码复用而继承。当你使用组合来实现代码复用的时候,是不会产生继承关系的。过度使用继承(通过“extends”关键字)的话,如果修改了父类,会损坏所有的子类。这是因为子类和父类的紧耦合关系是在编译期产生的。
不要仅仅为了多态而继承。如果你的类之间没有继承关系,并且你想要实现多态,那么你可以通过接口和组合的方式来实现,这样不仅可以实现代码重用,同时也可以实现运行时的灵活性。
这就是为什么四人帮(Gang of Four)的设计模式里更倾向于使用组合而不是继承的原因。面试者会在你的答案里着重关注这几个词语——“耦合”,“静态还是动态”,以及“发生在编译期还是运行时”。运行时的灵活性可以通过组合来实现,因为类可以在运行时动态地根据一个结果有条件或者无条件地进行组合。但是继承却是静态的。
Q.你能够通过实例来区别编译期继承和运行时继承,以及指出Java支持哪种吗?
A.“继承”表示动作和属性从一个对象传递到另外一个对象的场景。Java语言本身只支持编译期继承,它是通过“extends”关键字来产生子类的方式实现的,如下所示:
public class Parent { public String saySomething( ) { return “Parent is called”; } } public class Child extends Parent { @Override public String saySomething( ) { return super.saySomething( ) + “, Child is called”; } }
“Child”类的saySomething()方法的调用会返回“Parent is called,Child is Called”,因为,子类的调用继承了父类的“Parenet is called”。关键字“super”是用来调用“Parent”类的方法。运行时继承表示在运行时构建父/子类关系。Java语言本身不支持运行时继承,但是有一种替代的方案叫做“代理”或者“组合”,它表示在运行时组件一个层次对象的子类。这样可以模拟运行时继承的实现。在Java里,代理的典型实现方式如下:
public class Parent { public String saySomething( ) { return “Parent is called”; } } public class Child { public String saySomething( ) { return new Parent( ).saySomething( ) + “, Child is called”; } }
子类代理了父类的调用。组合可以按照下面的方式来实现:
public class Child { private Parent parent = null; public Child( ){ this.parent = new Parent( ); } public String saySomething( ) { return this.parent.saySomething( ) + “, Child is called”; } }
相关推荐
Java 编译与运行是Java开发中的基础环节,理解这一过程对于任何Java程序员来说都是至关重要的。Java由Sun Microsystems公司开发,始于1995年,最初名为Oak,后被Oracle公司接手运营。Java的独特之处在于它的跨平台...
Java 编译时多态和运行时多态 Java 编译时多态和运行时多态是 Java 语言中两个重要的概念,它们都是多态性的实现方式,但它们在实现机制和应用场景上有所不同。 编译时多态 编译时多态是指在编译期根据参数列表的...
反射允许程序在运行时动态获取类的信息并操作其成员,而注解则提供了一种元数据机制,可以在编译期或运行期对代码进行标记,以供工具或框架使用。 最后,书中通常会涵盖线程和并发编程。Java内置了强大的多线程支持...
13. **注解(Annotation)**:注解提供了一种元数据方式,可以用于编译期检查、运行时处理等。自定义注解可以增强代码的可读性和维护性。 14. **模块系统(Java 9+)**:Java 9引入了模块系统,有助于大型项目管理...
9. **反射和注解**:Java的反射机制允许在运行时检查类、接口、字段和方法的信息,而注解则提供了一种元数据的方式,用于在编译期或运行期进行代码的自定义处理。 10. **垃圾回收与内存管理**:Java的自动内存管理...
- **编译期**:使用`javac`命令将Java源代码编译成字节码文件。 - **运行期**: - 加载:JVM加载字节码文件。 - 校验:JVM对加载的字节码进行校验,确保其符合规范。 - 执行:JVM解释执行字节码,将其转化为目标...
2. **LISCENCE认证**:这个过程涉及验证用户是否有权运行特定的软件或使用特定的功能。在Java环境中,这可以通过在应用程序启动时检查许可证密钥来实现。如果许可证验证失败,应用程序可能会限制其功能或者完全无法...
- 运行期:JVM加载.class并运行.class(0和1) > 特点:跨平台、一次编译到处运行 - 名词解释: - JVM:java虚拟机 - 加载.class并运行.class - JRE:java运行环境 - 除了包含JVM以外还包含了运行java...
Java基础知识总结涵盖了Java程序设计语言的核心概念和常用知识点。...综上所述,Java基础知识不仅包括了语言的语法和基本结构,还涵盖了运行环境、开发工具以及重要的编程思想和设计模式,是学习和掌握Java编程的基石。
其次,"CertInfo.class"是Java编译后的字节码文件,它是Java源代码(如"CertInfo.java")经过javac编译器转换得到的。这个类文件很可能包含了实现数字证书读取功能的Java代码,允许程序在运行时解析和处理cer文件中...
Java运行时期包括编译期和运行期两部分。 JVM、JRE、JDK之间的关系是:JDK是Java开发工具包,包括JRE和编译器等工具;JRE是Java Runtime Environment,提供Java程序的运行环境;JVM是Java Virtual Machine,负责...
6. **AOP(面向切面编程)**:通过预编译方式或运行期动态代理实现在不修改源码的情况下,对程序进行功能增强,提升代码的模块化和可维护性。 7. **DAO(数据访问对象)层**:提供了一套标准的方式来访问数据库,...
AOP 是一种面向方面的编程技术,可以在编译期、字节码加载前、字节码后等多个阶段进行拦截和处理。 3. Java RASP 的实现细节 Java RASP 的实现细节包括 Instrumentation、ASM 等技术。Instrumentation 是 Java 中...
本文将深入探讨"JAVA范例 四)异常处理---编译时异常、运行时异常"这个主题,结合标签"源码"和"工具",我们可以理解这与实际编程实践和可能使用的开发工具如Struts2框架相关。 首先,我们来看编译时异常。编译时...
10. **注解(Annotation)**:介绍注解的定义、使用,以及元注解,如何自定义注解和注解处理器,以及它们在编译期和运行期的应用。 11. **JVM原理**:理解JVM的内存结构(堆、栈、方法区等),类加载机制,以及如何...
Java是一种广泛使用的面向对象的编程语言,它简单、面向对象、稳定且跨平台。...学习Java基础不仅仅是学习它的语法和编程模型,还要理解Java的运行环境、开发环境以及如何有效地使用Java平台进行软件开发。
在Java编程语言中,自定义编译期注解(Annotation)是一种强大的元数据工具,它允许程序员在代码中插入信息,这些信息可以在编译时或运行时被编译器或虚拟机读取并处理。自定义编译期注解不仅能够提供代码的文档功能...
- **环境变量设置**: 如何正确配置JAVA_HOME、PATH等环境变量,确保命令行可以正常编译和运行Java程序。 - **IDEA集成开发环境的使用**: IDEA是目前最流行的Java开发工具之一,学会使用它可以大大提高开发效率。 ##...
- **JRE** (Java Runtime Environment):Java运行环境,包括了Java虚拟机(JVM)和运行Java程序所需的类库。 - **配置环境变量**: - **JAVA_HOME**: 设置JDK的安装路径。 - **Path**: 添加%JAVA_HOME%\bin至PATH...