面向对象特征之二:继承

为什么要有继承

​ 多个类中存在相同属性和行为时,将这些内容抽取到单独一个类中,那么多个类无需再定义这些属性和行为,只要继承那个类即可。

  • 此处的多个类称为子类,单独的这个类称为父类(基类或超类)。可以理解为:“子类 is a 父类”

  • 类继承语法规则:

    class Subclass extends Superclass{ }

作用

  • 继承的出现提高了代码的复用性

  • 继承的出现让类与类之间产生了关系,提供了多态的前提

  • 不要仅为了获取其他类中某个功能而去继承。

继承的规则

说明

  • 子类继承了父类,就继承了父类的方法和属性

  • 在子类中,可以使用父类中定义的方法和属性,也可以创建新的数据和方法

  • 在Java 中,继承的关键字用的是“extends”,即子类不是父类的子集,而是对父类的“扩展”

规则

  • 子类不能直接访问父类中私有的(private)的成员变量和方法
  • Java只支持单继承,不允许多重继承
    • 一个子类只能有一个父类
    • 一个父类可以派生出多个子类

class SubDemo extends Demo{ } //ok

class SubDemo extends Demo1,Demo2...//error

  • 多重继承
  • 多层继承

练习

练习1:

  • 定义一个ManKind类,包括:

    • 成员变量int sexint salary
    • 方法void manOrWorman():根据sex的值显示“man”(sex==1)或者“woman”(sex==0);
    • 方法void employeed():根据salary的值显示“no job”(salary==0)或者“ job”(salary!=0)。
  • 定义类Kids继承ManKind,并包括:

    • 成员变量int yearsOld
    • 方法printAge()打印yearsOld的值。
  • 在Kids类的main方法中实例化Kids的对象someKid,用该对象访问其父类的成员变量及方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
public class ManKind {
private int sex;
private int salary;

public int getSex() {
return sex;
}

public void setSex(int sex) {
this.sex = sex;
}

public int getSalary() {
return salary;
}

public void setSalary(int salary) {
this.salary = salary;
}

public void manOrWorman(){
if (sex == 1){
System.out.println("man");
}
else if (sex == 0){
System.out.println("woman");
}
else System.out.println("输入有误!");
}
public void employeed(){
if (salary == 0){
System.out.println("no job");
}
else if (salary != 0){
System.out.println("has job");
}
else System.out.println("输入有误!");
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class Kids extends ManKind{
private int yearsOld;

public int getYearsOld() {
return yearsOld;
}

public void setYearsOld(int yearsOld) {
this.yearsOld = yearsOld;
}

public void printAge(){
System.out.println(yearsOld + " years old");
}
}
1
2
3
4
5
6
7
8
9
10
11
12
public class TestKids {
public static void main(String[] args) {
Kids someKid = new Kids();
someKid.setSex(1);
someKid.setSalary(50000);
someKid.setYearsOld(22);

someKid.manOrWorman();
someKid.employeed();
someKid.printAge();
}
}

练习2:根据下图实现类。在TestCylinder类中创建Cylinder类的对象,设置圆柱的底面半径和高,并输出圆柱的体积。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public class Circle {
private double radius;

public Circle(){
this.radius = 1;
}

public double getRadius() {
return radius;
}

public void setRadius(double radius) {
this.radius = radius;
}

public double findArea(){
return Math.PI * radius * radius;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public class Cylinder extends Circle{
private double length;

public Cylinder(){
this.length = 1;
}

public double getLength() {
return length;
}

public void setLength(double length) {
this.length = length;
}

public double findVolume(){
return findArea() * length;
}
}
1
2
3
4
5
6
7
8
public class TestCylinder {
public static void main(String[] args) {
Cylinder cylinder= new Cylinder();
cylinder.setRadius(2);
cylinder.setLength(4);
System.out.println("圆柱的体积为:" + cylinder.findVolume());
}
}

方法的重写:override

定义

​ 在子类中可以根据需要对从父类中继承来的方法进行改造,也称方法的重置、覆盖。在程序执行时,子类的方法将覆盖父类的方法

要求

  • 重写方法必须和被重写方法具有相同的方法名称、参数列表和返回值类型

  • 重写方法不能使用比被重写方法更严格的访问权限

  • 重写和被重写的方法须同时为static的,或同时为非static的

  • 子类方法抛出的异常不能大于父类被重写方法的异常。

四种访问权限修饰符

​ Java权限修饰符public、protected、private置于类的成员定义前,用来限定对象对该类对象成员的访问权限。

对于class的权限修饰只可以用public和default

  • public类可以在任意地方被访问。

  • default类只可以被同一个包内部的类访问。

关键字—super

作用

  • 在Java类中使用super来调用父类中的指定操作

    • super可用于访问父类中定义的属性
    • super可用于调用父类中定义的成员方法
    • super可用于在子类构造方法中调用父类的构造器
  • 注意:

    • 尤其当子父类出现同名成员时,可以用super进行区分
    • super的追溯不仅限于直接父类
    • super和this的用法相像,this代表本类对象的引用,super代表父类的内存空间的标识

调用父类的构造器

  • 子类中所有的构造器默认都会访问父类中空参数的构造器

  • 当父类中没有空参数的构造器时,子类的构造器必须通过this(参数列表)或者super(参数列表)语句指定调用本类或者父类中相应的构造器,且必须放在构造器的第一行

  • 如果子类构造器中既未显式调用父类或本类的构造器,默认调用的是父类空参的构造器,如果父类中又没有无参的构造器,则编译出错

    • 建议:设计一个类时,尽量要提供一个空参的构造器~

this和super的区别

子类对象的实例化过程

练习:类的继承,super

  • 写一个名为Account的类模拟账户。该类的属性和方法如下图所示。该类包括的属性:账号id,余额balance,年利率annualInterestRate;包含的方法:访问器方法(getter和setter方法),返回月利率的方法getMonthlyInterest(),取款方法withdraw(),存款方法deposit()

提示:在提款方法withdraw中,需要判断用户余额是否能够满足提款数额的要求,如果不能,应给出提示。

测试:写一个用户程序测试Account类。在用户程序中,创建一个账号为1122、余额为20000、年利率4.5%的Account对象。使用withdraw方法提款30000元,并打印余额。再使用withdraw方法提款2500元,使用deposit方法存款3000元,然后打印余额和月利率。

​ 运行结果如图所示:

  • 创建Account类的一个子类CheckAccount代表可透支的账户,该账户中定义一个属性overdraft代表可透支限额。在CheckAccount类中重写withdraw方法,其算法如下:
1
2
3
4
5
6
7
8
9
如果(取款金额<账户余额),
可直接取款
如果(取款金额>账户余额),
计算需要透支的额度
判断可透支额overdraft是否足够支付本次透支需要
如果可以
将账户余额修改为0,冲减可透支金额
如果不可以
提示用户超过可透支额的限额

提示:(1)子类CheckAccount的构造方法需要将从父类继承的3个属性和子类自己的属性全部初始化。(2)父类Account的属性balance被设置为private,但在子类CheckAccount的withdraw方法中需要修改它的值,因此应修改父类的balance属性,定义其为protected。

要求:写一个用户程序测试CheckAccount类。在用户程序中,创建一个账号为1122、余额为20000、年利率4.5%,可透支限额为5000元的CheckAccount对象。使用withdraw方法提款5000元,并打印账户余额和可透支额。再使用withdraw方法提款18000元,并打印账户余额和可透支额。再使用withdraw方法提款3000元,并打印账户余额和可透支额。

​ 运行结果如下图所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
public class Account {
protected int id; //账号
protected double balance; //余额
protected double annualInterestRate; //年利率

public Account(int id, double balance, double annualInterestRate) {
super();
this.id = id;
this.balance = balance;
this.annualInterestRate = annualInterestRate;
}

public int getId() {
return id;
}

public void setId(int id) {
this.id = id;
}

public double getBalance() {
return balance;
}

public void setBalance(double balance) {
this.balance = balance;
}

public double getAnnualInterestRate() {
return annualInterestRate;
}

public void setAnnualInterestRate(double annualInterestRate) {
this.annualInterestRate = annualInterestRate;
}

//返回月利率
public double getMonthlyInterest(){
return this.annualInterestRate/12;
}

//取钱
public void withdraw (double amount){
if (balance >= amount){
balance -= amount;
}else{
System.out.println("余额不足!");
}
}

//存钱
public void deposit (double amount){
balance += amount;
}
1
2
3
4
5
6
7
8
9
10
11
public class TestAccount {
public static void main(String[] args) {
Account account = new Account(1122,20000,0.045);
account.withdraw(30000);
System.out.println("您的账户余额为:" + account.getBalance());
account.withdraw(2500);
account.deposit(3000);
System.out.println("您的账户余额为:" + account.getBalance());
System.out.println("月利率为:" + account.getMonthlyInterest());
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
public class CheckAccount extends Account{
private double overdraft;//可透支额度

public CheckAccount(int id, double balance, double annualInterestRate, double overdraft) {
super(id, balance, annualInterestRate);
this.overdraft = overdraft;
}

public double getOverdraft() {
return overdraft;
}

public void setOverdraft(double overdraft) {
this.overdraft = overdraft;
}

public void withdraw (double amount){
if (amount <= balance){
balance -= amount;
}
else {
if (amount - balance <= overdraft){
overdraft -= (amount - balance);
balance = 0;
}
else {
System.out.println("超过可透支额的限额!");
}
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class TestCheckAccount {
public static void main(String[] args) {
CheckAccount checkAccount = new CheckAccount(1122,20000,0.045,5000);
checkAccount.withdraw(5000);
System.out.println("您的账户余额:" + checkAccount.getBalance());
System.out.println("您的可透支额:" + checkAccount.getOverdraft());
checkAccount.withdraw(18000);
System.out.println("您的账户余额:" + checkAccount.getBalance());
System.out.println("您的可透支额:" + checkAccount.getOverdraft());
checkAccount.withdraw(3000);
System.out.println("您的账户余额:" + checkAccount.getBalance());
System.out.println("您的可透支额:" + checkAccount.getOverdraft());
}
}

面向对象特征之三:多态性

基本概念

​ 可以理解为:一个事物的多种表现形态~

  • 多态性,是面向对象中最重要的概念,在java中有两种体现:
    • 方法的重载(overload)和重写(overwrite)。
    • 对象的多态性——可以直接应用在抽象类和接口上。

引用变量类型

  • Java引用变量有两个类型:编译时类型运行时类型。编译时类型由声明该变量时使用的类型决定,运行时类型由实际赋给该变量的对象决定。
    • 若编译时类型和运行时类型不一致,就出现多态(Polymorphism)

对象的多态

  • 对象的多态 —在Java中,子类的对象可以替代父类的对象使用
    • 一个变量只能有一种确定的数据类型
    • 一个引用类型变量可能指向(引用)多种不同类型的对象
1
2
3
Person p = new Student();
Object o = new Person();//Object类型的变量o,指向Person类型的对象
o = new Student(); //Object类型的变量o,指向Student类型的对象
  • 子类可看做是特殊的父类,所以父类类型的引用可以指向子类的对象:向上转型(upcasting)

  • 一个引用类型变量如果声明为父类的类型,但实际引用的是子类对象,那么该变量就不能再访问子类中添加的属性和方法

1
2
3
4
5
Student m = new Student();
m.school = “pku”; // 合法,Student类有school成员变量
Person e = new Student();
e.school = “pku”; // 非法,Person类没有school成员变量
// 属性是在编译时确定的,编译时e为Person类型,没有school成员变量,因而编译错误。

虚拟方法调用(Virtual Method Invocation)

正常的方法调用

1
2
3
4
Person e = new Person();
e.getInfo();
Student e = new Student();
e.getInfo();

虚拟方法调用(多态情况下)

1
2
Person e = new Student();
e.getInfo(); // 调用Student类的getInfo()方法

编译时类型和运行时类型

编译时e为Person类型,而方法的调用是在运行时确定的,所以调用的是Student类的getInfo()方法。——动态绑定

小结

前提

  • 需要存在继承或者实现关系
  • 要有覆盖操作

成员方法

  • 编译时:要查看引用变量所属的类中是否有所调用的方法。
  • 运行时:调用实际对象所属的类中的重写方法。

成员变量

  • 不具备多态性,只看引用变量所属的类。

子类继承父类

  • 子类重写了父类方法,就意味着子类里定义的方法彻底覆盖了父类里的同名方法,系统将不可能把父类里的方法转移到子类中
  • 对于实例变量则不存在这样的现象,即使子类里定义了与父类完全相同的实例变量,这个实例变量依然不可能覆盖父类中定义的实例变量

instanceof 操作符

x instanceof A:检验对象x是否为类A的对象,返回值为boolean型。

  • 要求x所属的类与类A必须是子类和父类的关系,否则编译错误。
  • 如果x属于类A的子类B,x instanceof A值也为true。

练习

练习1:建立 TestInstance 类,在类中定义方法 method1(Person e);

  • 在method1中:

    • 根据e的类型调用相应类的getInfo()方法。

    • 根据e的类型执行:

      • 如果e为Person类的对象,输出:

        1
        a person
      • 如果e为Student类的对象,输出:

        1
        2
        a student
        a person
      • 如果e为Graduate类的对象,输出:

        1
        2
        3
        a graduated student
        a student
        a person
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
public class TestInstance {
public static void main(String[] args) {
TestInstance t = new TestInstance();
t.method1(new Student());
System.out.println();
t.method1(new Graduate());
}

public void method1(Person p){
System.out.println(p.getInfo());

if (p instanceof Graduate){
System.out.println("a graduated student");
}
if (p instanceof Student){
System.out.println("a a student");
}
if (p instanceof Person){
System.out.println("a person");
}
}
}

class Person {
protected String name="person";
protected int age=50;
public String getInfo() {
return "Name: "+ name + "\n" +"age: "+ age;
}
}
class Student extends Person {
protected String school="heu";
public String getInfo() {
return "Name: "+ name + "\nage: "+ age
+ "\nschool: "+ school;
}
}
class Graduate extends Student{
public String major="IT";
public String getInfo()
{
return "Name: "+ name + "\nage: "+ age
+ "\nschool: "+ school+"\nmajor:"+major;
}
}

练习2多态—动态绑定

  • 定义三个类,父类 GeometricObject 代表几何形状,子类 Circle 代表圆形,MyRectangle代表矩形。
  • 定义一个测试类Test,编写equalsArea方法测试两个对象的面积是否相等(注意方法的参数类型,利用动态绑定技术),编写displayGeometricObject方法显示对象的面积(注意方法的参数类型,利用动态绑定技术)。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
public class GeometricObject {
protected String color;
protected double weight;

protected GeometricObject(String color, double weight) {
this.color = color;
this.weight = weight;
}

public String getColor() {
return color;
}

public void setColor(String color) {
this.color = color;
}

public double getWeight() {
return weight;
}

public void setWeight(double weight) {
this.weight = weight;
}

public double findArea(){
return 0.0;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public class Circle extends GeometricObject{
private double radius;

public Circle(double radius, String color, double weight) {
super(color, weight);
this.radius = radius;
}

public double getRadius() {
return radius;
}

public void setRadius(double radius) {
this.radius = radius;
}

public double findArea(){
return Math.PI * radius * radius;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
public class MyRectangle extends GeometricObject{
private double width;
private double height;

public MyRectangle(double width, double height, String color, double weight) {
super(color, weight);
this.width = width;
this.height = height;
}

public double getWidth() {
return width;
}

public void setWidth(double width) {
this.width = width;
}

public double getHeight() {
return height;
}

public void setHeight(double height) {
this.height = height;
}

public double findArea(){
return width * height;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
public class Test {
public static void main(String[] args) {
Test test = new Test();
Circle circle1 = new Circle(2.3, "Green", 1.0);
Circle circle2 = new Circle(2.3, "Red", 1.0);
MyRectangle myRectangle = new MyRectangle(2.3, 3.0, "blue", 2.0);
test.displayGeometricObject(circle1);

boolean b = test.equalsArea(circle1,circle2);
System.out.println(b);

}

// 判断两个对象的面积是否相等
public boolean equalsArea(GeometricObject g1, GeometricObject g2){
// if (g1.findArea() == g2.findArea())
// return true;
// else
// return false;
return g1.findArea() == g2.findArea();
}

// 显示对象的面积
public void displayGeometricObject(GeometricObject g){
System.out.println(g.findArea());
}
}

对象类型转换(Casting)

  • 基本数据类型的Casting:
    • 自动类型转换:小的数据类型可以自动转换成大的数据类型。如long g=20; double d=12.0f
    • 强制类型转换:可以把大的数据类型强制转换(casting)成小的数据类型。如 float f=(float)12.0; int a=(int)1200L
  • 对Java对象的强制类型转换称为造型
    • 从子类到父类的类型转换可以自动进行
    • 从父类到子类的类型转换必须通过造型(强制类型转换)实现
    • 无继承关系的引用类型间的转换是非法的
    • 在造型前可以使用instanceof操作符测试一个对象的类型

Object类

Object类是所有Java类的根父类

​ 如果在类的声明中未使用extends关键字指明其父类,则默认父类为Object类

1
2
3
4
5
6
7
8
9
10
11
public class Person {
...
}
等价于:
public class Person extends Object {
...
}

例:method(Object obj){…}//可以接收任何类作为其参数
Person o=new Person();
method(o);

Object类中的主要方法

==操作符与equals方法

==操作符

  • 基本类型比较值:只要两个变量的值相等,即为true。

  • 引用类型比较引用(是否指向同一个对象):只有指向同一个对象时,==才返回true。

​ 用“==”进行比较时,符号两边的数据类型必须兼容(可自动转换的基本数据类型除外),否则编译出错。

equals方法

​ 所有类都继承了Object,也就获得了equals()方法,还可以重写。

  • 只能比较引用类型,其作用与“==”相同,比较是否指向同一个对象。

  • 格式:obj1.equals(obj2)

  • 特例:当用equals()方法进行比较时,对类File、String、Date及包装类(Wrapper Class)来说,是比较类型及内容而不考虑引用的是否是同一个对象。

    • 原因:在这些类中重写了Object类的equals()方法

练习

练习1:编写Order类,有int型的orderId,String型的OrderName,相应的getter()和setter()方法,两个参数的构造器,重写父类的equals()方法:public boolean equals(Object obj),并判断测试类中创建的两个对象是否相等。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
/*
编写Order类,有int型的orderId,String型的OrderName,
相应的getter()和setter()方法,两个参数的构造器,
重写父类的equals()方法:public boolean equals(Object obj),并判断测试类中创建的两个对象是否相等。
*/
public class TestOrder {
public static void main(String[] args) {
Order o1 = new Order(1001,"AA");
Order o2 = new Order(1001,"AA");
System.out.println(o1 == o2);
System.out.println(o1.equals(o2));
}
}
class Order {
private int orderId;
private String orderName;

public Order(int orderId, String orderName) {
super();
this.orderId = orderId;
this.orderName = orderName;
}

public int getOrderId() {
return orderId;
}

public void setOrderId(int orderId) {
this.orderId = orderId;
}

public String getOrderName() {
return orderName;
}

public void setOrderName(String orderName) {
this.orderName = orderName;
}

public boolean equals(Object obj){
if (this == obj){
return true;
}
else if (obj instanceof Order){
Order o1 = (Order) obj;
return this.orderId == o1.orderId && this.orderName.equals(o1.orderName);
}
else
return false;
}
}

练习2:请根据以下代码自行定义能满足需要的MyDate类,在MyDate类中覆盖equals方法,使其判断当两个MyDate类型对象的年月日都相同时,结果为true,否则为false。 public boolean equals(Object o)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class TestEquals { 
public static void main(String[] args) {
MyDate m1 = new MyDate(14, 3, 1976);
MyDate m2 = new MyDate(14, 3, 1976);
if ( m1 == m2 ) {
System.out.println("m1==m2");
}
else {
System.out.println("m1!=m2"); //m1 != m2
}
if ( m1.equals(m2) ) {
System.out.println("m1 is equal to m2"); // m1 is equal to m2
}
else {
System.out.println("m1 is not equal to m2");
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
/*
在MyDate类中覆盖equals方法,使其判断当两个MyDate类型对象的年月日都相同时,结果为true,否则为false。public boolean equals(Object o)。
*/
public class TestMyDate {
public static void main(String[] args) {
MyDate m1 = new MyDate(14, 3, 1976);
MyDate m2 = new MyDate(14, 3, 1976);
if ( m1 == m2 ) {
System.out.println("m1==m2");
}
else {
System.out.println("m1!=m2"); //m1 != m2
}
if ( m1.equals(m2) ) {
System.out.println("m1 is equal to m2"); // m1 is equal to m2
}
else {
System.out.println("m1 is not equal to m2");
}
}
}

class MyDate {
private int day;
private int month;
private int year;

public MyDate(int day, int month, int year) {
super();
this.day = day;
this.month = month;
this.year = year;
}

public int getDay() {
return day;
}

public void setDay(int day) {
this.day = day;
}

public int getMonth() {
return month;
}

public void setMonth(int month) {
this.month = month;
}

public int getYear() {
return year;
}

public void setYear(int year) {
this.year = year;
}

public boolean equals(Object o){
if (this == o){
return true;
}
else if (o instanceof MyDate){
MyDate myDate = (MyDate) o;
return this.day == myDate.day && this.month == myDate.month && this.year == myDate.year;
}
else
return false;
}
}

toString()方法

  • toString()方法在Object类中定义,其返回值是String类型返回类名和它的引用地址

  • 在进行String与其它类型数据的连接操作时,自动调用toString()方法

1
2
3
Date now=new Date();
System.out.println(“now=” + now); //相当于
System.out.println(“now=” + now.toString());
  • 可以根据需要在用户自定义类型中重写toString()方法。如String类重写了toString()方法,返回字符串的值
1
2
3
s1=“hello”;
System.out.println(s1); //相当于
System.out.println(s1.toString());
  • 基本类型数据转换为String类型时,调用了对应包装类的toString()方法
1
2
int a=10;  
System.out.println(“a=” + a);

练习

练习:定义两个类,父类GeometricObject代表几何形状,子类Circle代表圆形。

写一个测试类,创建两个Circle对象,判断其颜色是否相等;利用equals方法判断其半径是否相等;利用toString()方法输出其半径。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
public class GeometricObject {
protected String color;
protected double weight;

public GeometricObject() {
super();
this.color = "white";
this.weight = 1.0;
}

public GeometricObject(String color, double weight) {
super();
this.color = color;
this.weight = weight;
}

public String getColor() {
return color;
}

public void setColor(String color) {
this.color = color;
}

public double getWeight() {
return weight;
}

public void setWeight(double weight) {
this.weight = weight;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
public class Circle extends GeometricObject{
private double radius;

public Circle() {
this.radius = 1.0;
}

public Circle(double radius) {
this.radius = radius;
}
public Circle(double radius,String color,double weight){
super(color, weight);
this.radius = radius;
}

public double getRadius() {
return radius;
}

public void setRadius(double radius) {
this.radius = radius;
}

//计算圆的面积
public double findArea(){
return 3.14 * radius * radius;
}

//比较两个圆的半径是否相等
public boolean equals(Object obj){
if (obj == this){
return true;
}
else if (obj instanceof Circle){
Circle c1 = (Circle) obj;
return this.radius == c1.radius;
}
else
return false;
}

//输出圆的半径
public String toString(){
// return radius + "";
return String.valueOf(radius);
}
}
1
2
3
4
5
6
7
8
9
public class TestCircle {
public static void main(String[] args) {
Circle circle1 = new Circle(2.3);
Circle circle2 = new Circle(2.3);
System.out.println(circle1.color == circle2.color);
System.out.println(circle1.equals(circle2));
System.out.println(circle1.toString());
}
}

包装类(Wrapper)

  • 针对八种基本定义相应的引用类型—包装类(封装类)

  • 有了类的特点,就可以调用类中的方法。

基本数据类型和包装类之间的转换

  • 基本数据类型包装成包装类的实例 — 装箱
    • 通过包装类的构造器实现: int i = 500; Integer t = new Integer(i);
    • 还可以通过字符串参数构造包装类对象:
1
2
Float f = new Float(“4.56”);
Long l = new Long(“asdf”); //NumberFormatException
  • 获得包装类对象中包装的基本类型变量 — 拆箱

    • 调用包装类的.xxxValue()方法:boolean b = xxx.booleanValue();
  • JDK1.5之后,支持自动装箱,自动拆箱,但类型必须匹配。

1
2
3
4
Integer a = 10; //自动装箱
Boolean b = false;

int b = a;//自动拆箱

与String类之间的转换

基本数据类型转换成字符串

  • 调用字符串重载的valueOf()方法: String str = String.valueOf(2.34f);

  • 更直接的方式:String intStr = 5 + “”

字符串转换成基本数据类型

  • 通过包装类的构造器实现:int i = new Integer(“12”);

  • 通过包装类的parseXxx(String s)静态方法:Float f = Float.parseFloat(“12.1”);

包装类转换成字符串

  • toString()方法

练习

练习:利用Vector代替数组处理:从键盘读入学生成绩(以负数代表输入结束),找出最高分,并输出学生成绩等级。

  • 提示:数组一旦创建,长度就固定不变,所以在创建数组前就需要知道它的长度。而向量类java.util.Vector可以根据需要动态伸缩

  • 创建Vector对象:Vector v=new Vector();

  • 给向量添加元素:v.addElement(obj); //obj必须是对象

  • 取出向量中的元素:Object obj=v.elementAt(0);

    • 注意第一个元素的下标是0,返回值是Object类型的。
  • 计算向量的长度:v.size();

  • 若与最高分相差10分内:A等;20分内:B等;30分内:C等;其它:D等

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
package L04.exer8;

import java.util.Scanner;
import java.util.Vector;

public class TestScore {
public static void main(String[] args) {
// 1、创建Scanner对象,提示从键盘输入学生的成绩(以负数代表输入结束)
Scanner scanner = new Scanner(System.in);
System.out.println("请输入学生的成绩:(以负数代表输入结束)");

// 2、foR(;;),从键盘依次获取学生的成绩,并填入由Vector v = new Vector()创建的对象v中
Vector v = new Vector();
int maxScore = 0;
for (;;){
int score = scanner.nextInt();
if (score < 0){
break;
}
// 3、求出所有输入的正的成绩中的最高分
// 获取最高分
if (maxScore < score){
maxScore = score;
}
//依次将成绩存入v中
v.addElement(score);
}

// 4、通过v.elementAt(i)依次获取填入v中的元素,同时判断一下各个分数的等级并输出
for (int i = 0; i < v.size(); i++){
Integer score = (Integer) v.elementAt(i);
char level;
if (maxScore - score <= 10){
level = 'A';
}else if (maxScore - score <= 20){
level = 'B';
}else if (maxScore - score <= 30){
level = 'C';
}else
level = 'D';
System.out.println("学生成绩为:" + score + " 等级为:" + level);
}
}
}

面试题

  • java类是否可以多继承,怎么实现多继承?

    答:java没有多继承,但可以通过接口的形式来达到多继承的目地。


  • 我比较两个String总是false,但是它们明明都是”abc” !
    答:比较String一定要使用equals或equalsIgnoreCase方法,不要使用 == !
      ==比较的是两个引用(变量)是否指向了同一个对象,而不是比较其内容。

  • int 和 Integer 有什么区别
    答:Java 提供两种不同的类型:引用类型和原始类型(或内置类型)。
    Int是java的原始数据类型,Integer是java为int提供的封装类。Java为每个原始类型提供了封装类。
    原始类型封装类,booleanBoolean,charCharacter,byteByte,shortShort,intInteger,longLong,floatFloat,doubleDouble
    引用类型和原始类型的行为完全不同,并且它们具有不同的语义。
    引用类型和原始类型具有不同的特征和用法,它们包括:大小和速度问题,这种类型以哪种类型的数据结构存储,当引用类型和原始类型用作某个类的实例数据时所指定的缺省值。对象引用实例变量的缺省值为 null,而原始类型实例变量的缺省值与它们的类型有关.

  • 定义类A 和类B 如下:【基础】
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
class A {
int a=1;
double d=2.0;
void show(){
System.out.println("Class A: a="+a +"\td="+d);
}
}
class B extends A{
float a=3.0f;
String d="Java program.";
void show(){
super.show( );
System.out.println("Class B: a="+a +"\td="+d);
}
}

(1) 若在应用程序的main方法中有以下语句:
A a=new A();
a.show();
则输出的结果如何?
(2) 若在应用程序的main方法中定义类B的对象b:
A b=new B();
b.show();
则输出的结果如何?
答:输出结果为:
1)Class A: a=1 d=2.0
2)Class A: a=1 d=2.0
Class B: a=3.0 d=Java program。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public void testString(){
String str1 = "小狗";
String str2 = "小狗";
String str3 = new String("小狗");
System.out.println(str1 == str2);//true
System.out.println(str1 == str3);//false
System.out.println(str1.equals(str3));//true
str1 = "小猫";
String str4 = "吃鱼";
String str5 = "小猫" + "吃鱼";
System.out.println(str1 == str5);//true

String str6 = (str2 + str4).intern();
System.out.println(str1 == str6);//false ---->true
}

  • 重载(overload)和重写(overried,有的书也叫做“覆盖”)的区别?Overload的方法是否可以改变返回值的类型?
    答:方法的重写Overriding和重载Overloading是Java多态性的不同表现。重写Overriding是父类与子类之间多态性的一种表现,重载Overloading是一个类中多态性的一种表现。如果在子类中定义某方法与其父类有相同的名称和参数,我们说该方法被重写 (Overriding)。子类的对象使用这个方法时,将调用子类中的定义,对它而言,父类中的定义如同被”屏蔽”了。如果在一个类中定义了多个同名的方法,它们或有不同的参数个数或有不同的参数类型,则称为方法的重载(Overloading)。Overloaded的方法是可以改变返回值的类型。

  • 写出程序结果
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
class Test
{
Test()
{
System.out.println("Test");
}
}
class Demo extends Test
{
Demo()
{
//super();
System.out.println("Demo");
}
public static void main(String[] args)
{
new Demo();
new Test();
}
}

//Test
//Demo
//Test

  • 写出程序结果(多态)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
class  A {
private int a;
public void setA(int a){
this.a = a;
}
public int getA(){
return a;
}
}
class B extends A {
private int a;
public void setA(int a){
this.a = a;
}
// public int getA(){
// return a;
//}
}
public class TestPerson {
public static void main(String[] args) {
A c = new B();
c.setA(5);
System.out.println(c.getA());
}
}
  • 写出程序结果
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
class Fu
{
boolean show(char a)
{
System.out.println(a);
return true;
}
}
class Demo extends Fu
{
public static void main(String[] args)
{
int i=0;
Fu f=new Demo();
Demo d=new Demo();
for(f.show('A'); f.show('B')&&(i<2);f.show('C'))
{
i++;
d.show('D');
}
}
boolean show(char a)
{
System.out.println(a);
return false;
}
}
A B

  • 写出程序结果:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
class Super
{
int i=0;
public Super(String a)
{
System.out.println("A");
i=1;
}
public Super()
{
System.out.println("B");
i+=2;
}
}
class Demo extends Super
{
public Demo(String a)
{
//super();
System.out.println("C");
i=5;
}
public static void main(String[] args)
{
int i=4;
Super d=new Demo("A");
System.out.println(d.i);
}
}
B C 5

  • 选择题,写出错误答案错误的原因,用单行注释的方式。
1
2
3
4
class Demo
{
int show(int a,int b){return 0;}
}

下面那些函数可以存在于Demo的子类中。
A. public int show(int a,int b){return 0;} //可以,覆盖。
B. private int show(int a,int b){return 0;} //不可以,权限不够。
C. private int show(int a,long b){return 0;} //可以,和父类不是一个函数。没有覆盖,相当于重载。
D. public short show(int a,int b){return 0;} //不可以,因为该函数不可以和给定函数出现在同一类中,或者子父类中。
E. static int show(int a,int b){return 0;} //不可以,静态只能覆盖静态。


  • 写出程序结果:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
class Fu
{
int num=4;
void show()
{
System.out.println("showFu");
}
}
class Zi extends Fu
{
int num=5;
void show()
{
System.out.println("showZi");
}
}
class T
{
public static void main(String[] args)
{
Fu f=new Zi();
Zi z=new Zi();
System.out.println(f.num);
System.out.println(z.num);
f.show();
z.show();
}
}

4
5
showZi
showZi

  • 写出程序结果
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class Super
{
int i=0;
public Super(String s)
{
i=1;
}
}
class Demo extends Super
{
public Demo(String s)
{
i=2;
}
public static void main(String[] args)
{
Demo d=new Demo("yes");
System.out.println(d.i);
}
}

//编译失败,因为父类中缺少空参数的构造函数。
//或者子类应该通过super语句指定要调用的父类中的构造函数。


  • 写出程序结果
1
2
3
4
5
6
7
8
9
10
11
12
13
class Super
{
public int get(){return 4;}
}
class Demo15 extends Super
{
public long get(){return 5;}
public static void main(String[] args)
{
Super s=new Demo15();
System.out.println(s.get());
}
}

//编译失败,因为子类父类中的get方法没有覆盖。但是子类调用时候不能明确返回的值是什么类型。所以这样的函数不可以存在子父类中。


  • 继承时候类的执行顺序问题,一般都是选择题,问你将会打印出什么?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
//父类:
package test;
public class FatherClass
{
public FatherClass()
{
System.out.println("FatherClass Create");
}
}
//子类:
package test;
import test.FatherClass;
public class ChildClass extends FatherClass
{
public ChildClass()
{
System.out.println("ChildClass Create");
}
public static void main(String[] args)
{
FatherClass fc = new FatherClass();
ChildClass cc = new ChildClass();
}
}

当执行如下操作
C:>java test.ChildClass
输出结果为:
FatherClass Create
FatherClass Create
ChildClass Create


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public class OuterClass { 
private double d1 = 1.0;
//insert code here
}
You need to insert an inner class declaration at line 3. Which two inner class declarations are
valid?(Choose two.)
A. class InnerOne{
public static double methoda() {return d1;}
}
B. public class InnerOne{
static double methoda() {return d1;}
}
C. private class InnerOne{
double methoda() {return d1;}
}
D. static class InnerOne{
protected double methoda() {return d1;}
}
E. abstract class InnerOne{
public abstract double methoda();
}

说明如下:
一.静态内部类可以有静态成员,而非静态内部类则不能有静态成员。 故 A、B 错
二.静态内部类的非静态成员可以访问外部类的静态变量,而不可访问外部类的非静态变量;return d1 出错。 故 D 错
三.非静态内部类的非静态成员可以访问外部类的非静态变量。 故 C 正确
四.答案为C、E


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class Test {
int x= 12;
public void method(int x) {
x+=x;
System.out.println(x);
}
}

Given:
Test t = new Test();
t.method(5);

What is the output from line 5 of the Test class?
A. 5
//B. 10
C. 12
D. 17
E. 24