文档章节

Java核心技术卷1: 继承

fzyz_sb
 fzyz_sb
发布于 2016/07/09 14:14
字数 3408
阅读 87
收藏 3

类,超类和子类

1. 关键字extends表明正在构造的新类派生于一个已存在的类.

2. 子类会自动继承超类的public/protected方法.

3. 超类的构造器必须被子类调用(默认构造函数会自动被调用). 所以, 以下代码会报错:

class A {
  private int i;
  A(int i) {
    this.i = i;
  }
}

class test extends A {
  test() {}
  public static void main(String[] args) {
    test t = new test();
  }
}

在通过扩展超类定义子类的时候, 仅需要指出子类与超类的不同之处. 因此在设计类的时候, 应该将通用的方法放在超类中, 而将具有特殊用途的方法放在子类中.

super的使用

super是一个指示编译器调用超类方法的特殊关键字, 它一般在两个地方使用:

1. 重载方法时, 子类调用父类的方法:

public double getSalary() {
  double baseSalary = super.getSalary();
  return baseSalary + bonus;
}

2. 子类构造器初始化父类构造器

public Manager(String n, double s, int year, int month, int day) {
  super(n, s, year, month, day);
  bonus = 0;
}

 

一个基本的Manager/Employee的例子, 用于讲述继承的方方面面:

inheritance/ManagerTest.java:

package inheritance;

/**
 * Created by lgt on 16/7/4.
 */
public class ManageTest {
    public static void main(String[] args) {
        Manager boss = new Manager("Carl Cracker", 80000);
        boss.setBonus(5000);

        Employee[] staff = new Employee[3];
        staff[0] = boss;
        staff[1] = new Employee("Harry Hacker", 50000);
        staff[2] = new Employee("Tommy Tester", 40000);

        for (Employee e: staff) {
            System.out.println("name:" + e.getName() + ",salary=" + e.getSalary());
        }
    }
}

inheritance/Employee.java:

package inheritance;

/**
 * Created by lgt on 16/7/4.
 */
public class Employee {
    private String name;
    private double salary;

    public Employee(String n, double s)
    {
        name = n;
        salary = s;
    }
    public String getName()
    {
        return name;
    }
    public double getSalary()
    {
        return salary;
    }
    public void raiseSalary(double byPercent)
    {
        double raise = salary * byPercent / 100;
        salary += raise;
    }
}

inheritance/Manager.java:

package inheritance;

/**
 * Created by lgt on 16/7/4.
 */
public class Manager extends Employee {
    private double bonus;

    public Manager(String n, double s)
    {
        super(n, s);
        bonus = 0;
    }
    public double getSalary()
    {
        double baseSalary = super.getSalary();
        return baseSalary + bonus;
    }
    public void setBonus(double b)
    {
        bonus = b;
    }
}

多态

一个对象变量可以指示多种实际类型的现象被称为多态.

在继承中, 子类的每个对象也是超类的对象. 例如上例中我们可以用Employee数组存储Manager对象, 但运行时候可以将staff[0]重新转换为Manager进行操作, 这里涉及到了动态绑定.

动态绑定

1. 编译器查看对象的声明类型和方法名.

2. 编译器将查看调用方法时提供的参数类型. 这里会进行重载解析, 例如x.f("hello")会调用f(String)而不是f(int)

3. 如果方法是private,static,final, 则执行静态绑定(因为private,static和final均不可继承, 所以不存在多态情况)

4. 调用动态绑定, 寻找最适合的那个方法.

  虚拟机会为每个类创建了一个方法表, 其中列出了所有方法的签名和实际调用的方法. 针对上例中的Employee和Manager, 会生成如下表:

Employee:

  getName() --> Employee.getName()

  getSalary() --> Employee.getSalary()

  raiseSalary(double) --> Employee.raiseSalary(double)

Manager:

  getName() --> Employee.getName()

  getSalary() --> Manager.getSalary()

  raiseSalary(double) --> Employee.raiseSalary(double)

  setBonus(double) --> Manager.setBonus(double)

这时候, e.getSalary()的解析过程如下:

4-1: 虚拟机提取e的实际类型的方法表.

4-2: 虚拟机搜索定义getSalary签名的类, 这时候虚拟机已经知道调用哪个方法.

4-3: 虚拟机调用这个方法.

final类和方法

不允许扩展的类为final类, 而不允许覆盖的方法可以为final方法. final类中所有的方法都是final方法.

final存在的目的是: 确保它们不会在子类中改变语义.

强制类型转换

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

2. 在将超类转换成子类之前, 应该使用instanceof进行检查(但一般情况下, 很少将超类转换为子类).

if (staff[1] instanceof Manager) {
  boss = (Manager) staff[1];
}

备注: 尽量少用强制类型转换和instanceof. 如果经常使用, 则代表类的设计不合理.

抽象类

abstract定义一个抽象类, 而子类必须实现抽象类中的抽象方法.

抽象类是不允许被实例化的.

 

Object: 所有类的超类

Object类是Java中所有类的始祖. 所以可以使用Object类型的变量引用任何类型的对象:

Object obj = new Employee("Harry Hacker", 35000);

equals方法

Object类中的equals方法用于检测一个对象是否等于另外一个对象, 它判断两个对象是否具有相同的引用. 但这实际上是毫无意义的, 例如两个String变量具有相同的值, 但它们并不是同一个引用, 而实际上我们会把相同值的String当做"equals".

所以一般情况下我们需要为类自定义equals方法:

public boolean equals(Object otherObject)
{
    if (this == otherObject) return true;

    if (otherObject == null) return false;

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

    Employee other = (Employee)otherObject;

    return Objects.equals(name, other.name)
            && salary == other.salary
            && Objects.equals(hireDay, other.hireDay);
}

1. 这里之所以要使用Objects.equals(name, other.name), 主要防止name为null从而导致的异常.

2. 这里也不能使用name == other.name, 因为Java中String的"=="实际上也是调用String.equals, 而equals判断是否是相同对象的引用, 而name和other.name明显是不同的对象, 只可能值相同而已.

如果是子类定义equals, 则需要调用父类的equals方法: if (!super.equals(otherObject)) return false; 具体的步骤如下:

1. 显式参数命名为otherObject, 稍后需要将它转换成另一个叫做other变量.

2. 检测this与otherObject是否引用同一个对象.

3. 检测otherObject是否为null, 如果为null, 则返回false.

4. 比较this与otherObject是否属于同一个类.

5. 将otherObject转换为相应的类类型变量.

6. 现在开始对所有需要比较的域进行比较. 使用==比较基本类型域, 使用equals比较对象域.

7. 如果在子类中重新定义equals, 就要在其中包含调用super.equals(other).

hashCode方法

散列码是由对象导出的一个整型值. 如果一个类定义了equals, 则通常需要重新定义hashCode.

散列码存在的意义在于: 标识一个对象. 所以对象的散列码通常为其内存存储地址, 这样就具有唯一性.

public int hashCode()
{
    return 7 * Objects.hashCode(name)
            + 11 * new Double(salary).hashCode() 
            + 13 * Objects.hashCode(hireDay);
}

但更好的方法是使用Objects.hash方法:

public int hashCode()
{
    return Objects.hash(name, salary, hireDay);
}

 

泛型数组列表

我们可以使用ArrayList来定义泛型数组列表, 具体的实例如下:

import java.util.ArrayList;

/**
 * Created by lgt on 16/6/25.
 */
public class Test {
    public static void main(String[] args)
    {
        ArrayList<String> s = new ArrayList<>();
        s.add("hello");
        s.add("world");
        s.add("i");
        s.add("you");
        s.add(3, "love");
        // [hello, world, i, love, you]
        System.out.println(s);
    }
}

对象包装器与自动装箱

在ArrayList中, 尖括号的类型必须为对象. 所以如果使用int, 则我们需要将其转换为对象. 所有的基本类型都有一个与之对应的类, 如Integer类对应基本类型int. 这些类称为包装器. 对象包装器是不可变的, 一旦构造了包装器, 就不允许更改包装器在其中的值. 同时, 对象包装器类还是final, 因此不能定义它们的子类.

如果我们想将字符串转换为int, 则需要使用parseInt()方法:

自动装箱:

ArrayList<Integer> list = new ArrayList<>();

则执行list.add(3), 会自动变换为list.add(Integer.valueOf(3));

自动拆箱:

int n = list.get(i), 会自动变换为int n = list.get(i).intValue();

 

反射

能够分析类能力的程序成为反射. 反射机制可以用来:

1. 在运行中分析类的能力.

2. 在运行中查看对象.

3. 实现通用的数组操作代码.

4. 利用Method对象.

Class类

在程序运行期间, Java运行时系统始终为所有的对象维护一个被称为运行时的类型标识. 这个信息跟踪着每个对象所属的类. 虚拟机利用运行时类型信息选择相应的方法执行.

可以通过Class类来访问这些信息. Object类中的getClass()方法将会返回一个Class类型的实例.

获取Class的三种方法:

1. 使用Object.getClass()

Class c1 = boss.getClass();
// inheritance.Manager
System.out.println(c1.getName());

2. 调用静态方法forName获得类名对应的Class对象

try {
    String className = "java.util.Date";
    Class c2 = Class.forName(className);
    // java.util.Date
    System.out.println(c2.getName());
} catch (Exception e) {
    e.printStackTrace();
}

3. 如果T是任意的Java类型, 则T.class将代表匹配的类对象.

Class c3 = int.class;
// int
System.out.println(c3.getName());

 

虚拟机为每个类型管理一个Class对象. 所以可以使用==运算符实现两个类对象比较的操作:

if (e.getClass() == Employee.class) {...}

而newInstance()可以用来快速的创建一个类的实例, 它调用默认的构造函数初始化新创建的对象. 如果这个类没有默认的构造器, 则抛出一个异常:

public class PersonTest {
    public static void main(String[] args)
    {
        Employee e1 = new Employee("Harry Hacker", 50000);

        // true
        System.out.println(e1.getClass() == Employee.class);
        try {
            Employee e2 = e1.getClass().newInstance();
            // 0.0
            System.out.println(e2.getSalary());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

利用反射分析类的能力

反射机制最重要的内容是: 检查类的结构.

在java.lang.reflect包中有三个类Field, Method和Constructor分别用于描述类的域,方法和构造器.

1. 它们都有getName方法, 用于返回项目的名称. 也有getModifiers方法, 用来返回描述public/static等修饰符使用情况. 而Modifier类的静态方法isPublic/isPrivate/isFinal都可以判断方法或构造器是否为public,private或final.

2. Field类有getType方法, 用来返回描述域所属类型的Class对象.

3. Method有getReturnType方法, 用来描述返回值的类型.

4. Class类中的getFields, getMethods和getConstructors方法将分别返回类提供的public域,方法和构造器数组, 其中包括超类的公有成员. Class类的getDeclareFields, getDeclareMethods和getDeclaredConstructors方法将分别返回类中声明的全部域.

一个实际的例子: 打印一个类的全部信息的方法:

package reflection;

import java.util.*;
import java.lang.reflect.*;

/**
 * Created by lgt on 16/7/5.
 */
public class ReflectionTest {
    public static void main(String[] args)
    {
        String name;
        if (args.length > 0) name = args[0];
        else {
            Scanner in = new Scanner(System.in);
            System.out.println("Enter class name (e.g. java.util.Date):");
            name = in.next();
        }

        try {
            Class c1 = Class.forName(name);
            Class superC1 = c1.getSuperclass();
            String modifiers = Modifier.toString(c1.getModifiers());
            if (modifiers.length() > 0) System.out.print(modifiers + " ");
            System.out.print("class " + name);
            if (superC1 != null && superC1 != Object.class) System.out.print("( extends " + superC1.getName());

            System.out.print("\n{\n");
            printConstructors(c1);
            System.out.println();
            printMethods(c1);
            System.out.println();
            printFields(c1);
            System.out.println("}");
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
        System.exit(0);
    }

    public static void printConstructors(Class c1)
    {
        Constructor[] constructors = c1.getConstructors();
        for (Constructor c: constructors) {
            String name = c.getName();
            System.out.print("    ");
            String modifiers = Modifier.toString(c.getModifiers());
            if (modifiers.length() > 0) System.out.print(modifiers + " ");
            System.out.print(name + "(");

            Class[] paramTypes = c.getParameterTypes();
            for (int j = 0; j < paramTypes.length; j++) {
                if (j > 0) System.out.print(", ");
                System.out.print(paramTypes[j].getName());
            }
            System.out.println(");");
        }
    }

    public static void printMethods(Class c1)
    {
        Method[] methods = c1.getDeclaredMethods();
        for (Method m: methods) {
            Class retType = m.getReturnType();
            String name = m.getName();

            System.out.print("    ");
            String modifiers = Modifier.toString(m.getModifiers());
            if (modifiers.length() > 0) System.out.print(modifiers + " ");
            System.out.print(retType.getName() + " " + name + "(");

            Class[] paramTypes = m.getParameterTypes();
            for (int j = 0; j < paramTypes.length; j++) {
                if (j > 0) System.out.print(",");
                System.out.print(paramTypes[j].getName());
            }
            System.out.println(");");
        }
    }

    public static void printFields(Class c1)
    {
        Field[] fields = c1.getDeclaredFields();
        for (Field f: fields) {
            Class type = f.getType();
            String name = f.getName();
            System.out.print("    ");
            String modifiers = Modifier.toString(f.getModifiers());
            if (modifiers.length() > 0) System.out.print(modifiers + " ");
            System.out.println(type.getName() + " " + name + ";");
        }
    }
}

假设输入: java.lang.Double, 则输出如下:

public final class java.lang.Double( extends java.lang.Number
{
    public java.lang.Double(double);
    public java.lang.Double(java.lang.String);

    public boolean equals(java.lang.Object);
    public static java.lang.String toString(double);
    public java.lang.String toString();
    public int hashCode();
    public static int hashCode(double);
    public static double min(double,double);
    public static double max(double,double);
    public static native long doubleToRawLongBits(double);
    public static long doubleToLongBits(double);
    public static native double longBitsToDouble(long);
    public volatile int compareTo(java.lang.Object);
    public int compareTo(java.lang.Double);
    public byte byteValue();
    public short shortValue();
    public int intValue();
    public long longValue();
    public float floatValue();
    public double doubleValue();
    public static java.lang.Double valueOf(java.lang.String);
    public static java.lang.Double valueOf(double);
    public static java.lang.String toHexString(double);
    public static int compare(double,double);
    public static boolean isNaN(double);
    public boolean isNaN();
    public static boolean isInfinite(double);
    public boolean isInfinite();
    public static boolean isFinite(double);
    public static double sum(double,double);
    public static double parseDouble(java.lang.String);

    public static final double POSITIVE_INFINITY;
    public static final double NEGATIVE_INFINITY;
    public static final double NaN;
    public static final double MAX_VALUE;
    public static final double MIN_NORMAL;
    public static final double MIN_VALUE;
    public static final int MAX_EXPONENT;
    public static final int MIN_EXPONENT;
    public static final int SIZE;
    public static final int BYTES;
    public static final java.lang.Class TYPE;
    private final double value;
    private static final long serialVersionUID;
}

在运行时使用反射分析对象

一般查看任意对象的数据域名称和类型的步骤为:

1. 获得对应的Class对象.

2. 通过Class对象调用getDeclaredFields.

但如果我们想查看指定域的值, 则关键的方法为Field类中的get方法. 如果f是一个Field类型的对象, obj是某个包含f域的类的对象, f.get(obj)将返回一个对象, 其值为obj域的当前值. 例如:

Employee harry = new Employee("Harry Hacker", 35000); 
Class c1 = harry.getClass(); Field f = c1.getDeclaredField("name"); 
Object v = f.get(harry); 

这里, 代码是有问题的, 因为name是私有的, 所以我们需要使用setAccessible方法设置其可访问.

一个实例:

ObjectAnalyzerTest.java:

package objectAnalyzer;

import java.util.ArrayList;
/**
 * Created by lgt on 16/7/5.
 */
public class ObjectAnalyzerTest {
    public static void main(String[] args) {
        ArrayList<Integer> squares = new ArrayList<>();
        for (int i = 1; i <= 5; i++) {
            squares.add(i * i);
        }
        System.out.println(new ObjectAnalyzer().toString(squares));
    }
}

ObjectAnalyzer.java:

package objectAnalyzer;

import java.lang.reflect.AccessibleObject;
import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.ArrayList;


/**
 * Created by lgt on 16/7/5.
 */
public class ObjectAnalyzer {
    private ArrayList<Object> visited = new ArrayList<>();

    /*
    * */
    public String toString(Object obj) {
        if (obj == null) return "null";
        if (visited.contains(obj)) return "...";
        visited.add(obj);

        Class c1 = obj.getClass();
        if (c1 == String.class) return (String) obj;
        if (c1.isArray()) {
            String r = c1.getComponentType() + "[]{";
            for (int i = 0; i < Array.getLength(obj); i++) {
                if (i > 0) r += ",";
                Object val = Array.get(obj, i);
                if (c1.getComponentType().isPrimitive()) r += val;
                else r += toString(val);
            }
            return r + "}";
        }

        String r = c1.getName();
        do {
            r += "[";
            Field[] fields = c1.getDeclaredFields();
            AccessibleObject.setAccessible(fields, true);
            for (Field f: fields) {
                if (!Modifier.isStatic(f.getModifiers())) {
                    if (!r.endsWith("[")) r += ",";
                    r += f.getName() + "=";
                    try {
                        Class t = f.getType();
                        Object val = f.get(obj);
                        if (t.isPrimitive()) r += val;
                        else r += toString(val);
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            }
            r += "]";
            c1 = c1.getSuperclass();
        } while (c1 != null);

        return r;
    }
}

输出为:

java.util.ArrayList[elementData=class java.lang.Object[]{java.lang.Integer[value=1][][],java.lang.Integer[value=4][][],java.lang.Integer[value=9][][],java.lang.Integer[value=16][][],java.lang.Integer[value=25][][],null,null,null,null,null},size=5][modCount=5][][]

使用反射编写泛型数组

package arrays;

import java.lang.reflect.*;
import java.util.*;

/**
 * Created by lgt on 16/7/5.
 */
public class CopyOfTest {
    public static void main(String[] args) {
        int[] a = {1, 2, 3};
        a = (int[]) goodCopyOf(a, 10);
        // [1, 2, 3, 0, 0, 0, 0, 0, 0, 0]
        System.out.println(Arrays.toString(a));
        String[] b = {"Tom", "Dick", "Harry"};
        b = (String[]) goodCopyOf(b, 10);
        // [Tom, Dick, Harry, null, null, null, null, null, null, null]
        System.out.println(Arrays.toString(b));
    }

    public static Object goodCopyOf(Object a, int newLength) {
        Class c1 = a.getClass();
        if (!c1.isArray()) return null;
        Class componentType = c1.getComponentType();
        int length = Array.getLength(a);
        Object newArray = Array.newInstance(componentType, newLength);
        System.arraycopy(a, 0, newArray, 0, Math.min(length, newLength));
        return newArray;
    }
}

 

© 著作权归作者所有

fzyz_sb
粉丝 417
博文 209
码字总数 447144
作品 0
武汉
程序员
私信 提问
一些Java编程类书转让(新)

为了资源重复利用,现在有一些编程类书转让。 新旧程度为9-9.5成新,书中无记号。 下面标识的价格是我购买时的价格,我转让的价格为: 以下标识50元以下的书每本在标识价格基础上减去10元 以...

匿名网友
2011/01/24
746
13
一些编程书转让

为了资源重复利用,现在有一些编程类书转让。 新旧程度为9成新,书中无记号。 价格:50以下书-10RMB,以上的书-20RMB 希望能一次购买多本,这样快递费用能省不少。或者当面提书。 当面提书地...

匿名网友
2011/01/23
695
12
Java 书籍 Top 10

陈皓 http:// blog.csdn.net/haoel 下面是Java Inside上推荐的十本Java书籍(文章来源),我把中文版的也列了出来。 1)Java Language Specification, Third Edition (by James Gosling) 本书...

JavaGG
2009/09/21
12.7K
20
Java程序员必读书单,家族又添新成员

点击关注异步图书,置顶公众号 每天与你分享IT好书 技术干货 职场知识 参与文末话题讨论,每日赠送异步图书。 ——异步小编 有些革命出其不意地吸引了全世界的眼球。Twitter、Linux操作系统和...

异步社区
2018/05/09
0
0
java学习路线及资源下载,持续整理中

学习路线图:http://blog.csdn.net/shenzhq1980/article/details/48470337 1、java学习经典书籍_基础编程篇 下载地址:http://blog.csdn.net/shenzhq1980/article/details/48375543 书籍: ......

迷茫80
2015/09/21
571
1

没有更多内容

加载失败,请刷新页面

加载更多

Nginx 快速安装详解

一、Nginx Nginx (engine x) 是一个高性能的HTTP和反向代理web服务器,同时也提供了IMAP/POP3/SMTP服务。Nginx是由伊戈尔·赛索耶夫为俄罗斯访问量第二的Rambler.ru站点(俄文:Рамбле...

网络小虾米
30分钟前
4
0
技术分享 | slave_relay_log_info 表认知的一些展开

作者:胡呈清 slave_relay_log_info 表是这样的: mysql> select * from mysql.slave_relay_log_info\G *************************** 1. row *************************** Number_of_lin......

爱可生
33分钟前
3
0
nginx配置http访问自动跳转到https

server {listen 80;server_name www.域名.com;rewrite ^(.*) https://$server_name$1 permanent;}server {listen 443;server_name www.域名.com;root /home/www;ssl on;......

很好亦平凡ms
33分钟前
6
0
SpreadJS:一款中国研发的类Excel开发工具,功能涵盖Excel的 95% 以上

Excel 作为一款深受用户喜爱的电子表格工具,借助其直观的界面、出色的计算性能、数据分析和图表,已经成为数据统计领域不可或缺的软件之一。 基于Excel对数据处理与分析的卓越表现,把Excel...

葡萄城技术团队
33分钟前
4
0
用javafx框架tornadofx做了个天气预报的程序

class WeatherApp : App(WeatherView::class)class WeatherView : View("十五天天气预报") { val weatherVM: WeatherViewModel by inject() val controller: WeatherController by......

oschina4cyy
37分钟前
4
1

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部