`
fengyong0912
  • 浏览: 106991 次
  • 性别: Icon_minigender_1
  • 来自: 武汉
社区版块
存档分类
最新评论

java 多太性的综合运用

    博客分类:
  • JAVA
阅读更多

或许大家java的多态问题,对上溯,下溯造型有了一定的概念,对protect和private大家想必也很清楚,但是,这几个个结合在一起,往往令人产生很多困惑,在这里,我举一个例子,大家或许会发觉这篇文章对你来说还是很有意义的:

  例子一共有两个class. 可能出现困惑的地方我都会在后面一一解释。

  A是一个父类,B继承A,并且实现了protectedTest(Object obj)方法.如下面所示:

  B.java的源代码:


package cn.org.matrix.test;
import cn.org.matrix.test.A;
/**
* <p>Title: protect, private and upcasting </p>
* <p>Description: email:chris@matrix.org.cn</p>
* <p>Copyright: Matrix Copyright (c) 2003</p>
* <p>Company: Matrix.org.cn</p>
* @author chris
* @version 1.0,who use this example pls remain the declare
*/
public class B extends A
{
protected int protectedb = 0;
protected int protectedab = 0;


protected void protectedTest(Object obj)
{
System.out.println("in B.protectedTest(Object):" + obj);
}
}


  A.java的源代码:

package cn.org.matrix.test;
import cn.org.matrix.test.B;
/**
* <p>Title: protect, private and upcasting </p>
* <p>Description: email:chris@matrix.org.cn</p>
* <p>Copyright: Matrix Copyright (c) 2003</p>
* <p>Company: Matrix.org.cn</p>
* @author chris
* @version 1.0,who use this example pls remain the declare
*/

public class A
{
protected int protecteda = 0;
protected int protectedab = 0;
private void privateTest()
{
System.out.println("in A.privateTest()");
}
protected void protectedTest(Object obj)
{
System.out.println("in A.protectedTest(Object):" + obj );
}

protected void protectedTest( String str )
{
System.out.println("in A.protectedTest(String):" + str);
}

public static void main (String[] args)
{
// Test A
A a1 = new A();
a1.privateTest();
// Test B
String helloStr = "Hello";
Object helloObj = helloStr;
B b1 = new B();
A a2 = b1; // 这里发生了什么?困惑1
b1=a1; //编译错误,困惑2
b1. privateTest(); //编译错误,困惑3
b1.protectedTest(helloObj); //输出结果?困惑4
b1.protectedTest(helloStr); //编译错误,困惑5
a2.protectedTest(helloObj); //输出结果? 困惑6
a2.protectedTest(helloStr); //输出结果?困惑7 ?
}
}

下面,我来逐个解释每一处困惑的地方:

  困惑1:

  这里其实就是子类自动上溯造型到父类A。这里a2其实是指向了一个B类型的对象. 我们通常都可以这样作: A a2=b1, 这样作的意思实际上就是让a2指向了一个类型B的对象-在这里就是b1了。

  在java里面,关于跨类引用,有两条规则应该记住:

  1. 如果a是类A的一个引用,那么,a可以指向类A的一个实例,或者说指向类A的一个子类。

  2. 如果a是接口A的一个引用,那么,a必须指向实现了接口A的一个类的实例。

  所以,根据这两个规则,我们就不难理解例子中的A a2 = b1是什么意思了。

  困惑2:

  A a2 = b1是可以的,但是为什么b1=a1却是不行? 在这里,我们依然可以套用上面的两条规则,我们可以看到,b1是类B的一个引用,a1既不是类B的实例,也不是类B的子类的实例,所以直接b1=a1就出现了编译错误.
如果确实需要进行这样的转化,我们可以这样作:b1=(B)a1; 进行强制转化,也就是下溯造型. 在java里面,上溯造型是自动进行的,但是下溯造型却不是,需要我们自己定义强制进行.

  困惑3:

  b1. privateTest();编译不通过? 这是很显然的,你可以回顾一下private的定义: 私有域和方法只能被定义该域或方法的类访问. 所以,在这里,b1不能访问A的方法privateTest(),即使b1是A的子类的实例.
请看下面的例子:

public class A
{
private int two(int i) { return i; }
}
class Test extends A {
public static void main(String[] args) {
System.out.println(A.two(3));
}
}

  System.out.println(A.two(3));这行编译出错,显然,因为private方法不能在这个类之外被访问。

  而protected则不同,我们回顾一下protected的定义: 被保护的域或方法只能被类本身、类的子类和同一 程序包中的类所访问。

  下面是一个错误使用protected的例子:

package cn.org.matrix.test;
public class ProtectedTest {
protected void show() {
System.out.println("I am in protected method");
}
}

import cn.org.matrix.test.*;
public class Test {
public static void main (String[] args) {
ProtectedTest obj = new ProtectedTest();
obj.show();
}
}

  因为访问权限问题,你会得到"show() has protected access in test.ProtectedTest"的出错信息.

困惑4:

  b1.protectedTest(helloObj); 输出的是"in B.protectedTest(Object):…" 这到底是为什么呢? 为什么jvm能够确定是输出B的方法而不是A的方法? 这就和jvm的运行机制有关系了. 我们上面提到了,a1是一个A类型的引用,但是指向了一个B类型的实例. 在这里,如果jvm根据引用的类型-在这里就是A 来定义调用哪个方法的话,那么应该是调用A的protectedTest(helloObj).

  然后实际上不是这样的,因为jvm的动态编译能力,jvm会在run-time来决定调用哪一个method,而不是在compile time. 也就是所谓的late-binding(run-time)和early-binding(compile-time).

  困惑5:

b1.protectedTest(helloStr); 这里为什么会出现编译错误? 他可以调用类B的protectedTest(Object obj)方法啊,把helloStr上溯造型成一个object就行了啊..或者上溯造型到A然后调用A的protectedTest(helloStr)方法。

  问题的根源就在于此了,既然有两种选择,jvm应该选择那一种?这种不确定性如果交给jvm来动态决定的话,势必带来程序的不确定性..虽然java在其他的一些地方也有类似的情形出现,比如static变量的循环定义造成的不确定性,但是,在这里,jvm还是在编译阶段就解决了这个问题。

  所以,我们会在这一步遇到编译错误: "reference to protectedTest is ambiguous; both method protectedTest(java.lang.String) in mytest.A and method protectedTest(java.lang.Object) in mytest.B match at line 46.

  在这里,我们遇到的是显式的reference ambiguous错误,但是,有时候,隐式的reference ambiguous却往往是更加的危险。

  在这里,我举个例子:

  父类的 源代码:

public super
{
private void test(int i, long j);
{
System.out.println(i+"and"+j);
}
}
子类的源代码:
public sub
{
private void test(long j, int i);
{
System.out.println(i+"and"+j);
}
}

  子类和父类都用有相同名称的方法test,参数类型不同而已.这种情况下,编译可以被通过.
但是如果你在另外一个类中用到了如下代码:

Sub sb = new Sub();
sb.test(100, 3000);

  你就会遇到编译错误,因为没有确定的指出3000的类型,所以造成reference ambiguous的错误了.
 困惑6:

  a2.protectedTest(helloObj);

  输出结果分别是:"in B.protectedTest(Object).." 经过上面的解释,想必大家都能很清楚的知道为什么会有这两个输出结果了:a2.protectedTest(helloObj);因为jvm的late-binding,所以在run-time的时候,调用了B类的方法,虽然在编译期间a2只是一个父类A的引用类型。

  困惑7:

  a2.protectedTest(helloStr); 为什么这里会输出" in A.protectedTest(Object)…"。为什么这里不会编译出错?为什么b1. protectedTest(helloStr)会出错而a2. protectedTest(helloStr)会出错?我调用了a2.equals(b1)和a2==b1得到的结果都是true啊?但是为什么这里出这个错误?

  在这里,这个问题是最关键的,也是我们放到最后来解答的原因。

  首先,回顾一下equals()和==的在java里面的概念,记得有一道scjp的题目:

  题目:下面的哪些叙述为真。

  A. equals()方法判定引用值是否指向同一对象。

  B. == 操作符判定两个分立的对象的内容和类型是否一致。

  C. equals()方法只有在两个对象的内容一致时返回true。

  D. 类File重写方法equals()在两个分立的对象的内容和类型一致时返回true。

  答案是AD,严格来说这个问题的答案是不确定的,因为equals()方法是可以被重载的,但是如果新类没有重写equals(),则该方法在两个变量指向同一对象时返回真. 实际上java也是推荐的是使用equals()方法来判断两个对象的内容是否一样,就像String类的equals()方法所做的那样,判定两个String对象的内容是否相同。而==操作符返回true的唯一条件是两个变量指向同一对象。

  在这里,我们不再深入的讨论关于equals()和==的区别和概念。我们只需要知道,在我们的例子里面,无论是equals()和==都是一个含义-就是引用值是否指向同一个对象(因为我们并没有重写equals()).
显然,我们在进行了a2=b1.这一步之后,a2和b1都是指向同一个对象。

  既然指向同一个对象,为什么还要区别对待a2和b1?为什么a2就没有编译错误,而b1就要遇到reference ambiguous错误?

  我们现看看jvm规范里的一段话:

"The Java Virtual Machine does not require any particular internal
structure for objects. In Sun's current implementation of the Java Virtual Machine, a reference to a class instance is a pointer to a handle that is itself a pair of pointers: one to a table containing the methods of the object and a pointer to the Class object that represents the type of the object, and the other to the memory allocated from the Java heap for the object data."

  实际上就是说:在java虚拟机中,类实例的引用就是指向一个句柄(handle)的指针,这个句柄是一对指针:一个指针指向一张表格,实际上这个表格也有两个指针:一个指针指向一个包含了对象的方法表,另外一个指向类对象;另一个指针指向一块从java堆中为分配出来内存空间。

  那么,在a2=b1的时候,到底发生了什么?

  实际上,在a2=b1的时候,仍然是存在两个句柄,a2和b1,但是a2和b1拥有同一块数据内存块和不同的函数表。所以在a2.protectedTest(helloStr)的时候,jvm会从a2的函数表里找到protectedTest(String Str)方法,但是b1.protectedTest(helloStr)却会出现编译错误,因为jvm从b1的函数表里找不到,然后就选择自己上溯造型还是参数上溯造型出现了ambiguous。这也是我们对这个问题的精确解释。

 

分享到:
评论

相关推荐

    JAVA做员工管理系统

    【JAVA做员工管理系统】是一个基于Java编程...总的来说,【JAVA做员工管理系统】是一个综合运用了Java编程、数据库技术、Web开发框架和软件工程方法的实例,对于学习和理解企业级应用的开发流程具有很高的参考价值。

    学生管理系统Java综合练习

    此外,你还需要考虑类之间的关系,如继承、封装和多态性。 3. **集合框架**:在处理多个学生对象时,Java的集合框架如ArrayList或LinkedList将被用到。它们允许我们存储和管理一组对象,实现增删改查功能。 4. **...

    多种设计模式综合练习

    通过实践这些设计模式,开发者可以学习到如何在代码中引入多态性、封装和抽象,从而编写出更加高效、灵活的软件。此外,这个项目对于准备面试或提升自身技能的开发者来说,也是一份宝贵的资源,因为它提供了设计模式...

    java面向对象程序综合设计课程设计

    在Java中,多态性主要通过方法重写(Override)和接口实现来实现。通过多态,可以编写更加灵活和可扩展的代码,使得程序能够处理不同类型的对象,而无需关心具体类型。 4. 类与对象:在面向对象编程中,类是对象的...

    面向对象编程 JAVA编程综合实验报告.doc

    通过这个实验,学生能够深入理解Java语言的面向对象特性,学会利用类和对象进行程序设计,以及如何在实际项目中运用封装、继承和多态性。同时,他们还了解了如何在Java环境中配置和调试代码,这对于后续的Java编程...

    Java综合性实验-魔板游戏.zip

    在这个项目中,你可以看到如何定义游戏的各种元素(如角色、棋盘、规则等)为类,以及如何通过继承、封装和多态性来组织代码。 3. **Swing或JavaFX GUI**:Java提供了Swing和JavaFX库来创建GUI。在这个游戏中,可能...

    Java程序设计实验与实训源代码

    实验可能会设计出各种类和对象的实例,通过继承和多态性来实现代码复用和模块化设计。 3. **异常处理**: 在Java中,异常处理是程序健壮性的重要组成部分。通过try-catch-finally语句块,学习者可以学习如何捕获和...

    java基础综合案例--发红包【界面版】

    通过类的封装、继承和多态性,可以构建出灵活且可扩展的系统结构。 2. **异常处理**:在发放和接收红包过程中,可能会出现各种异常情况,比如余额不足、红包金额分配错误等。因此,编写代码时需要加入异常处理机制...

    Java课程实验2

    多态性让不同的对象能对同一消息作出不同的响应,增加了程序的灵活性。 其次,异常处理是Java程序健壮性的关键。Java使用try-catch-finally语句块来捕获和处理运行时错误。学生需要学会如何妥善处理可能出现的异常...

    java写的简单校园卡管理系统_java项目_java学生一卡通_java系统_校园_eclipse饭卡

    包括面向对象编程(OOP)概念,如类、对象、继承、封装和多态性,以及异常处理、集合框架(如ArrayList、HashMap)和IO流等。 2. **Swing或JavaFX GUI**:为了实现用户友好的界面,开发者可能使用了Swing或JavaFX来...

    java期末考试试卷b卷答案

    简答题常常涉及对继承、多态性等面向对象核心概念的讨论,这些概念不仅是Java编程的基础,也是区分Java与其他编程语言的重要特征。多态性允许同一操作作用于不同的对象类型,而产生不同的行为效果,这一点在Java中...

    JAVA游戏大综合及各种特效

    例如,我们可以创建Game类作为游戏的主类,Player类代表玩家角色,Enemy类表示敌人,Bullet类表示子弹等,通过继承和多态性来构建复杂的游戏逻辑。 在2D游戏开发中,Java提供了AWT和Swing库,用于构建图形用户界面...

    Java 实验代码及文档

    实验可能涉及单一继承、多态性以及super关键字的使用。 实验3:抽象类和接口 抽象类是不能被实例化的类,常用于定义通用接口。实验3可能讨论了如何创建抽象类,使用abstract关键字,以及理解抽象方法。接口是另一种...

    java 城堡游戏项目.zip

    这样的游戏设计需要开发者具备扎实的Java基础,包括类与对象的创建、继承、多态性以及封装等面向对象编程概念。此外,事件处理、线程管理和状态机的设计也是实现游戏动态性的关键。 首先,项目中的"projectok_x...

    赛跑游戏(Java)

    首先,我们需要理解Java多线程的概念。在Java中,多线程允许程序同时执行多个任务,提高了程序的并发性。在这个赛跑游戏中,每个赛跑者都是一个单独的线程,它们并行运行,模拟了真实比赛中的竞争状态。Java提供了两...

    Java游戏高级编程(开发篇)

    这包括对Java语言的基本语法的理解,如变量、数据类型、控制流语句(if、for、while等)、类和对象的创建、继承、封装和多态性。同时,深入理解面向对象编程的概念,如抽象、接口和内部类,也是十分必要的。此外,...

    用Java实现的飞机大战小游戏

    其次,多态性是面向对象编程的重要特性。在游戏中,不同的飞机可能需要响应不同的事件,比如友机可能会有躲避、射击的行为,而敌机则可能是撞击、爆炸。这些不同的行为在Java中可以通过重写父类方法实现,这就是多态...

    java源码超市系统

    1. **Java基础**:整个系统的核心是Java编程语言,它提供了面向对象的特性,如封装、继承和多态性,使得代码结构清晰,易于维护。此外,Java的异常处理机制和垃圾回收机制确保了程序的稳定性和内存管理的有效性。 2...

    JAVA综合面试题.doc

    - **多态性**:多态使得不同类的对象能响应相同的消息,提高了代码的灵活性和可扩展性。 2. **基本数据类型与引用类型**: - **基本数据类型**如`int`是Java中的原始类型,它们直接存储值。 - **引用类型**如`...

Global site tag (gtag.js) - Google Analytics