首页 > 编程 > Java > 正文

Java02继承

2019-11-11 02:51:22
字体:
来源:转载
供稿:网友

5继承

5.1 类、超类和子类

关键字extends表示继承。

java中的继承都是公用继承,没有C++中的私有继承和保护继承。

 

Super class  Subclass  来自于集合的术语

Base class   Derived class

Parent class  Child class

 

将通用的功能放在超类中,将具有特殊用途的方法放在子类中。

 

子类中可以覆盖(override)超类中的方法。

子类不能直接访问超类的私有域。

子类中调用超类方法:super.method()

C++中用 [超类名::方法]的方式调用超类方法。

 

因为子类不能访问超类私有域,

子类的构造器中,第一条语句要用super(...)调用超类构造器;

如果未提供super()语句,则调用默认无参数的超类构造器;

如果超类没有默认构造器,则编译出错。

 

C++中利用初始化列表语法调用超类构造器。

 

继承层次:

由一个公共超类派生出来的所有类的集合被称为继承层次(inheritance hierarchy)

从某个特定的类到其祖先的路径被称为该类的继承链(inheritance chain)

一个祖先类可以拥有多个子孙继承链。

Java不支持多继承。

 

多态:

IS-A规则:子类的每个对象也是超类的对象。

置换法则,程序中出现超类对象的任何地方都可以用子类对象置换。

超类对象变量可以引用任何其子类对象。

不能将超类的引用赋给子类变量。

 

静态绑定 static binding:

对于PRivate方法、static方法、final方法或者构造器,编译器可明确知道应该调用哪个方法。

动态绑定:

调用的方法依赖于隐式参数的实际类型,并且在运行时实现动态绑定。

虚拟机预先为每个类创建一个方法表(method table),其中列出了所有方法的签名和实际调用的方法。真正调用的时候,虚拟机查找这个表。

 

覆盖一个方法时,子类方法不能低于超类方法的可见性。

 

阻止继承:

Final类:不允许被扩展的类。

Final方法:不允许被重写的方法。

Final类中的方法自动的称为final方法。

Final域:常量。

Final的主要目的:确保它们不会在子类中改变语义。

String类是final类。

 

如果一个方法没有被覆盖并且很短,编译器进行内联(inlining)优化处理。因为分支转移会扰乱指令预取策略。

 

强制类型转换:

用一对圆括号将目标类名括起来,置于需要转换的目标引用之前。

唯一原因:在暂时忽视对象的实际类型之后,使用对象的全部功能。

可以将子类引用赋给超类变量,但当超类引用赋给子类变量时,必须进行类型转换。

在类型转换之前,进行instanceof检查。

综上所述:

1、只能在继承层次内进行类型转换;

2、在将超类转换成子类之前,使用instanceof进行检查。

 

在一般情况下,应该尽量少用类型转换和instanceof运算符。

 

C++的类型转换:Manager* boss = dynamic_cast<Manager*>(staff[1]);

 

抽象类:

包含一个或多个抽象方法的类必须声明为抽象类。

抽象类可以包含具体数据和具体方法。

 

其子类如果实现部分抽象方法,则也需声明为抽象类;

子类若实现全部抽象方法,则不必声明为抽象方法。

 

类即使不包含抽象方法,也可以将其声明为抽象类。

抽象类不能被实例化。

可以定义一个抽象类的对象变量,但是只能引用非抽象子类的对象。

 

C++中的抽象类:只要包含纯虚函数的类就是抽象类。

 

编译器只允许调用在类中声明的方法。

 

受保护访问:

允许子类的方法访问超类的某个域,或希望超类的某些方法被子类访问。

protected

最好的示例:Object类中的clone方法。

 

Private——对本类可见;

Public——对所有类可见;

Protected——对本包和所有子类可见;

默认——对本包可见。

 

5.2 Object:所有类的超类

Object类型的变量可以引用任何类型的对象。

在java中,只有基本类型(primitive types)不是对象。

所有数组类型都是对象,扩展于Object类。

 

equals方法:

    public boolean equals( Object other);

Object中,检测两个对象变量是否有相同的引用(功能与==一致)。

在子类定义equals方法时,首先调用超类的equals方法。如果检测失败,对象就不可能相等。如果超类中的域都相等,就需要比较子类中的实例域。

 

Java要求equals语法具有下面的性质:

自反性

对称性

传递性

一致性

X.equals(null)应该返回false

 

重写符号:方法前一行加@override

 

重写equals方法步骤:

1、显式参数命名为otherObject;

2、检测this与otherObject是否引用同一个对象;if (this == otherObject) return true;

3、检测otherObject是否为空;if (otherObject == null) return false;

4、检测otherObject是否属于同一类。

a) 如果equals的语义在每个子类中有所改变,用getClass检测:

if (getClass() != otherObject.getClass()) return false;

b) 如果所有的子类都拥有统一语义,就使用instanceof检测:

if (!(otherObject instanceof ClassName)) return false;

5、将otherObject转换为相应的类类型变量:ClassName other = (ClassName) otherObject;

6、对所有需要比较的域进行比较。需在其中包含调用super.equals(other);

 

数组类型的域,用Arrays.equals(a1, a2)检查是否相等。

 

hashcode方法:

public int hashcode();

定义在Object类中,每个对象都有一个默认的散列码,其值为对象的存储地址。

Equals方法必须和hashcode方法一致,即如果a.equals(b)为真,则a和b的散列码必须相同。

 

Objects.hashCode(Object a); //如果为Null,返回0,否则返回a.hashcode();

Objects.hash(Object... objects); //返回所有对象的散列码组合。

 

数组类型的域,用Arrays.hashCode(a)计算散列码。

 

toString方法

Object类定义了toString()方法,打印输出对象所属的类名和散列码。

绝大多数遵循格式:类的名字[域值]

用getClass().getName()获得类的名字。

只要对象与字符串通过 + 相连,就自动调用对象的toString()方法。

System.out.println(x); //自动调用x的toString()方法。

 

数组继承了object类的toString方法。

如果想打印数组的内容,用Arrays.toString(a);多维数组用Arrays.deepToString(a);

 

强烈建议为自定义的每一个类增加toString方法。

 

@SuppressWarnings(“unchecked”)

 

包装器(wrapper)  自动装箱(autoboxing)  自动拆箱

 

5.3 参数数量可变方法

最后一个参数为 类名... args,实际传入一个数组。调用时可以传入多个类对象,甚至一个类数组。

5.4 枚举类

所有枚举类型都是Enum类的子类。

比较时,直接用“==”

 

5.6 反射

反射库(reflection library)提供了一个非常丰富且精心设计的工具集,以便编写能够动态操纵java代码的程序。这项功能被大量地应用于JavaBeans中,它是Java组件的体系结构。

能够分析类能力的程序称为反射(reflective)。

 

在程序运行期间,系统保存了所有已加载类的信息,虚拟机用这些信息选择相应的方法执行。

保存这些信息的类被称为Class。

获得类对象的三种方法:

1、Object类中的getClass()方法将会返回一个Class类型的实例。

2、还可以通过调用静态方法forName获得类名对应的Class对象。

String className = “java.util.Date”;

Class c1 = Class.forName(className);

当包含main()方法的类被加载时,将会递归加载全部所需类。

一种逐步加载方法,调用Class.forName()手工加载其他类。

3、Class cl1 = Data.class;

 

可以利用==运算符实现两个类对象的比较操作。

 

类对象的newInstance()方法可以快速创建一个类的实例,调用的是默认构造函数。

String s = “java.util.Date”;

Object m = Class.forName(s).newInstance();

 

利用反射分析类的能力:

反射机制最重要的内容——检查类的结构。

在java.lang.reflect包中用三个类Field、Method、Constructor分别用于描述类的域、方法和构造函数。

Class类中的getFileds()、getMethods()、getConstructors()将返回public域、方法、构造器数组,包括超类的公有成员。

Class类中的getDeclareFiels()、getDeclaredMehtods()、getDeclaredConstructors()返回全部域、方法、构造器数组,不包括超类的公有成员。

 

public class ReflectionTest{    public static void main(String[] args)    {        String name;        Scanner in = new Scanner(System.in);        System.out.print("Enter class name: ");        name = in.next();        StringBuilder sb = new StringBuilder();        try        {            Class c1 = Class.forName(name);            Class superc1 = c1.getSuperclass();            String modifiers =Modifier.toString(c1.getModifiers());            if ( modifiers.length() > 0 )                sb.append(modifiers + " ");            sb.append("class" + name);            if (superc1 != null && superc1 != Object.class)                sb.append(" extends " + superc1.getName());            sb.append("/n{/n");            Constructor[]constructors = c1.getDeclaredConstructors();            for (Constructor c : constructors)            {                String cname = c.getName();                sb.append("/t");                String cmodifier =Modifier.toString(c.getModifiers());                if (cmodifier.length() != 0)                sb.append(cmodifier + " ");                sb.append(cname+"(");                Class[]cparameters = c.getParameterTypes();                for ( int j = 0; j < cparameters.length; j++)                {                    if (j > 0)                        sb.append(",");                    sb.append(cparameters[j].getName());                }                sb.append(");/n");            }            sb.append("/n/n");            Method[]methods = c1.getDeclaredMethods();            for (Method m : methods)            {                sb.append("/t");                String mmodifier =Modifier.toString(m.getModifiers());                if (mmodifier.length() != 0)                    sb.append(mmodifier + " ");                String rtype = m.getReturnType().getName();                sb.append(rtype + " " + m.getName() + "(");                Class[]mparameters = m.getParameterTypes();                for (int i = 0; i < mparameters.length; i++)                {                    if (i > 0)                        sb.append(",");                    sb.append(mparameters[i].getName());                }                sb.append(")/n");            }            sb.append("/n/n");            Field[]fields = c1.getDeclaredFields();            for (Field f : fields)            {                sb.append("/t");                 String fmodifier =Modifier.toString(f.getModifiers());                if( fmodifier.length() !=0 )                sb.append(fmodifier + " ");                Class type = f.getType();                String tname = type.getName();                String fname = f.getName();                sb.append(tname + " " + fname + ";/n");            }            sb.append("}");            System.out.println(sb);        }        catch(ClassNotFoundException e)        {            e.printStackTrace();        }    }}

利用反射机制查看域值:

f.setAcessible(true);

f.get(obj);

利用反射机制修改域值:

f.get(obj, value);

 

编写一个可供任意类使用的通用toString方法:

1、使用getDeclaredFileds获得所有数据域;

2、使用setaccessible将所有的域设置为可访问的;

3、对每个域,获得名字和值。

 

setAccessible方法是AccessibleObject类中的一个方法,是Filed、Method、Constructor类的公共超类。

 

Method对象的invoke方法,允许调用包装在当前Method对象中的方法。

方法签名:

Object invoke(Object obj, Object... args);

第一个参数为具体对象,其余为这个方法的参数。

对于静态方法,第一个参数设置为NULL。

Invoke的参数和返回值必须是Object类型的。

使用反射获得方法指针的代码要比仅仅直接调用方法明显慢一些。

 

5.7 继承设计的技巧

1、将公共操作和域放在超类;

2、不要使用受保护的域;

3、使用继承体现IS-A关系;

4、除非所有继承的方法都有意义,否则不要使用继承;

5、在覆盖原方法时,不要改变预期的行为;

6、使用多态;

7、不要过多的使用反射——对编写系统程序及其有用,不适用于应用程序的编写。

 


上一篇:Java02继承

下一篇:java访问controller示例

发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表