Java基础之面向对象(中)
目录
2.虚拟方法调用(Virtual Method Invocation)
1.至少独立完成一遍以上的项目代码
2.积累完成项目的过程中常见的bug的调试
方式一:“硬”看,必要时,添加输出语句。
方式二:Debug
3.捋顺思路,强化逻辑
4.对象、数组等内存结构的解析
5.遵守编码的规范,标识符的命名规范等
6.在类前,方法前,方法内具体逻辑的实现步骤等添加必要的注释
一、面向对象特征之二: 继承性(inheritance)
1.为什么要有继承?
多个类中存在相同属性和行为时,将这些内容抽取到单独一个类中, 那么多个类无需再定义这些属性和行为,只要继承那个类即可。
此处的多个类称为子类(派生类),单独的这个类称为父类(基类或超类)。可以理解为:“子类 is a 父类”
2.类继承语法规则
class Subclass extends SuperClass{ }
3.作用
继承的出现减少了代码冗余,提高了代码的复用性。
继承的出现,更有利于功能的扩展。
继承的出现让类与类之间产生了关系,提供了多态的前提。
注意:不要仅为了获取其他类中某个功能而去继承
子类继承了父类,就继承了父类的方法和属性。
在子类中,可以使用父类中定义的方法和属性,也可以创建新的数据和 方法。
在Java 中,继承的关键字用的是“extends”,即子类不是父类的子集, 而是对父类的“扩展”。
Java只支持单继承和多层继承,不允许多重继承
一个子类只能有一个父类 一个父类可以派生出多个子类
class SubDemo extends Demo{ } //ok
class SubDemo extends Demo1,Demo2...//error
4.代码演示
package com.tyl.java;
/*
* 面向对象的特征之二:继承性
一、继承性的好处:
①减少了代码的冗余,提高了代码的复用性
②便于功能的扩展
③为之后多态性的使用,提供前提
*
*
* 二.继承性的格式, class A extends B{}
* A:子类、派生类、subc1ass
B:父类、超类、基类、superclass
2.1体现:一旦子类A继承父类B以后,子类A中就获取了父类B中声明的所有的属性和方法。
特别的,父类中声明为private的属性或方法,子类继承父类以后,仍然认为获取了父类中私有的结构。
只有因为封装性的影响,使得子类不能直接调用父类的结构而已。
2,2子类继承父类以后,还可以声明自己特有的属性或方法:实现功能的拓展。
子类和父类的关系,不同于子集和集合的关系。
extends:延展、扩展
三、java只支持单继承和多层继承,不允许多重继承
1.java中的单继承性,一个子类只能有一个父类
2.一个父类可以派生出多个子类
3.子父类是相对的概念
4.子类直接继承的父类,称为:直接父类。间接继承的父类称为:间接父类
5.子类继承父类以后,就获取了直接父类以及所有间接父类中声明的属性和方法
四、 1、如果我们没有显式的声明一个类的父类的话,则此类继承于jaVa.1ang.0 bject类
2、所有的java类(除java,lang.Object类之外)都直接或间接的继承于java.lang.Object类
3、意味着,所有的java类具有java.lang.Object:类声明的功能。
*
*/
public class ExtendsTest {
public static void main(String[] args) {
Person p1 = new Person();
//p1.age = 5;
//System.out.println(p1.age);
p1.eat();
System.out.println("''''''''''''''''''''''''''''''''''''''''''");
Student s1 = new Student();
s1.eat();
//s1.sleep();
s1.name = "tyl";
s1.setAge(10);
System.out.println(s1.getAge());
s1.breath();
Creature c1 = new Creature();
c1.breath();
System.out.println(c1.toString()); // object是Creature的父类
}
}
5.练习1
package com.wc.java;
public class Circle {
private double redius; // 半径
public Circle() {
redius = 1.0;
}
public double getRedius() {
return redius;
}
public void setRedius(double redius) {
this.redius = redius;
}
//返回圆的面积
public double findArea(){
return Math.PI*redius*redius;
}
}
package com.wc.java;
public class Cylinder extends Circle {
private double length; //高
public Cylinder(){
length = 1.0;
}
public double getLength() {
return length;
}
public void setLength(double length) {
this.length = length;
}
//返回圆柱的体积
public double findVolume(){
// return Math.PI * getRedius() * getRedius() * getLength();
return findArea() * getLength();
}
}
package com.wc.java;
public class CylinderTest {
public static void main(String[] args) {
Cylinder cylinder = new Cylinder();
cylinder.setRedius(2.1);
cylinder.setLength(3.4);
double volume = cylinder.findArea();
System.out.println("圆柱的体积为:" + volume);
double area = cylinder.findArea();
System.out.println("底面面积为: " + area);
}
}
二、Eclipse 的 Debug 功能
如何调试
1. 设置断点
注意:可以设置多个断点
2. debug as java application
3. 常用操作
三、方法的重写 (override/overwrite)
1.定义
在子类中可以根据需要对从父类中继承来的方法进行改造,也称 为方法的重置、覆盖。在程序执行时,子类的方法将覆盖父类的方法。
2.要求
1. 子类重写的方法必须和父类被重写的方法具有相同的方法名称、参数列表
2. 子类重写的方法的返回值类型不能大于父类被重写的方法的返回值类型
3. 子类重写的方法使用的访问权限不能小于父类被重写的方法的访问权限 ;子类不能重写父类中声明为private权限的方法
4. 子类方法抛出的异常不能大于父类被重写方法的异常
注意: 子类与父类中同名同参数的方法必须同时声明为非static的(即为重写),或者同时声明为 static的(不是重写)。因为static方法是属于类的,子类无法覆盖父类的方法。
3.代码演示
package com.overwrite.java;
public class Person {
String name;
int age;
public Person(){
}
public Person(String name, int age) {
this.name = name;
this.age = age;
}
static void eat(){
System.out.println("吃饭");
}
public void walk(int distance){
System.out.println("走路" + distance + "公里");
}
public Object info(){
return null;
}
public double info1(){
return 1.0;
}
}
package com.overwrite.java;
public class Student extends Person{
String major;
public Student() {
}
public Student(String major) {
this.major = major;
}
public void study(){
System.out.println("学习专业是:" + major);
}
//overrides com.overwrite.java.Person.eat 对父类中的eat方法进行了重写
public static void eat(){
System.out.println("学生多吃有营养的食物");
}
public String info(){
return null;
}
// public int info1(){
// return 1;
// }
@Override
public void walk(int distance) {
// TODO Auto-generated method stub
System.out.println("重写的方式");
}
}
package com.overwrite.java;
/*
* 方法的重写(override/overwrite)
1.重写:子类继承父类以后,可以对父类中同名同参数的方法,进行覆盖操作
2.应用:重写以后,当创建子类对象以后,通过子类对象调用子父类中的同名同参数的方法时,实际执行的是子类重写父类的方法
3.重写的规定:
方法的声明:权限修饰符 返回值类型 方法名(形参列表)throws 异常的类型{
//方法体
}
约定俗称:子类中的叫重写的方法,父类中的叫被重写的方法
①子类重写的方法的方法名和形参列表与父类被重写的方法的方法名和形参列表相同
②子类重写的方法的权限修饰符不小于父类被重写的方法的权限修饰符
>特殊情况:子类不能重写父类中声明为private权限的方法
③返回值类型:
>父类被重写的方法的返回值类型是Void,则子类重写的方法的返回值类型只能是Void
>父类被重写的方法的返回值类型是A类型,则子类重写的方法的返回值类型可以是A类或A类的子类
>父类被重写的方法的返回值类型是基本数据类型(比如:double),则子类重写的方法的返回值类型必须是相同的数据类型(必须是double)
④ 子类重写的方法抛出的异常类型不大于父类被重写的方法抛出的异常类型(具体放到异常处理时候再讲)
...........................................................................
子类和父类中的同名同参数的方法要么都声明为非static的(考虑重写),要么都声明为static的(不是重写)。
*
* 面试题: 区分方法的重载与重写
*
*/
public class PersonTest {
public static void main(String[] args) {
Student s= new Student("电子信息");
s.eat();
s.walk(10);
s.study();
Person p = new Person();
p.eat();
}
}
四、四种访问权限修饰符
五、关键字:super
1.在Java类中使用super来调用父类中的指定操作
super可用于访问父类中定义的属性
super可用于调用父类中定义的成员方法
super可用于在子类构造器中调用父类的构造器
注意:
尤其当子父类出现同名成员时,可以用super表明调用的是父类中的成员
super的追溯不仅限于直接父类
super和this的用法相像,this代表本类对象的引用,super代表父类的内存空间的标识
2.调用父类的构造器
子类中所有的构造器默认都会访问父类中空参数的构造器
当父类中没有空参数的构造器时,子类的构造器必须通过this(参数列表)或者super(参数列表)语句指定调用本类或者父类中相应的 构造器。同时,只能”二选一” ,且必须放在构造器的首行
如果子类构造器中既未显式调用父类或本类的构造器,且父类中又 没有无参的构造器,则编译出错
3.this和super的区别
package com.overwrite.java;
public class Person1 {
String name;
int age;
int id = 1001; //身份证号
public Person1(){
super();
System.out.println("我无处不在");
}
public Person1(String name) {
super();
this.name = name;
}
public Person1(String name, int age) {
this(name);
this.age = age;
}
public void eat(){
System.out.println("吃饭");
}
public void walk(int distance){
System.out.println("走路" + distance + "公里");
}
}
package com.overwrite.java;
public class Student1 extends Person1{
String major;
int id = 1002; //学号
public Student1() {
}
public Student1(String major) {
this.major = major;
}
public Student1(String name, int age, String major){
super(name, age); //调用父类构造器
this.major = major;
}
public void study(){
System.out.println("学习专业是:" + major);
this.eat();
super.eat();
walk(11);
}
@Override
public void eat(){
System.out.println("学生多吃有营养的食物");
}
public void show(){
System.out.println("name =" + this.name + ", age = " + super.age);
System.out.println("id :" + this.id); // 当前类中
System.out.println("id :" + super.id); // 父类之中
System.out.println("专业是 :" + major);
}
}
package com.overwrite.java;
/*
* super关键字的使用
1,super理解为:父类的
2,super可以用来调用:属性、方法、构造器
3.super的使用
3.1我们可以在子类的方法或构造器中。通过使用"super.属性"或"super,方法"的方式,显式的调用
父类中声明的属性或方法。但是,通常情况下,我们习惯省略"super."
3.2特殊情况:当子类和父类中定义了同名的属性时,我们要想在子类中调用父类中声明的属性,则必须显式的
使用"super,属性"的方式,表明调用的是父类中声明的属性。
3.3特殊情况:当子类重写了父类中的方法以后,我们想在子类的方法中调用父类中被重写的方法时,则必须显式的
使用"super,方法"的方式,表明调用的是父类中被重写的方法。
4.super调用构造器
4,1 我们可以在子类的构造器中显式的使用"super(形参列表)"的方式,调用父类中声明的指定的构造器
4.2 "super(形参列表)"的使用,必须声明在子类构造器的首行!
4.3 我们在类的构造器中,针对于"this(形参列表)"或"super(形参列表)"只能二选一,不能同时出现
4.4 在构造器的首行,没有显式的声明"this(形参列表)"或"super(形参列表)",则默认调用的是父类中空参的构造器
4.5 在类的多个构造器中,至少有一个类的构造器中使用了"supr(形参列表)",调用父类中的构造器
*
*/
public class SuperTest {
public static void main(String[] args) {
Student1 s = new Student1();
s.show();
System.out.println("................................");
s.study();
Student1 s1 = new Student1("tyl", 22, "IT");
s1.show();
System.out.println("...................");
Student1 s2 = new Student1();
}
}
六、子类对象实例化过程
子类对象实例化的全过程
1,从结果上来看:(继承性)
子类继承父类以后,就获取了父类中声明的属性或方法。
创建子类的对象,在堆空间中,就会加载所有父类中声明的属性。
2.从过程上来看:
当我们通过子类的构造器创建子类对象时,我们一定会直接或间接的调用其父类的构造器,进而调用父类的父类的构造器,
直到调用了java,1ang.Object类中空参的构造器为止。正因为加载过所有的父类的结构,所以才可以看到内存中有
父类中的结构,子类对象才可以考虑进行调用。
明确:虽然创建子类对象时,调用了父类的构造器,但是自始至终就创建过一个对象,即为new的子类对象。
思考: 1).为什么super(…)和this(…)调用语句不能同时在一个构造器中出现?首行
2).为什么super(…)或this(…)调用语句只能作为构造器中的第一句出现?
答:无论通过哪个构造器创建子类对象,需要保证先初始化父类。
目的:当子类继承父类后,"继承"父类中所有的属性和方法,因此子类有必要知道父类如何为对象进行初始化。
1.实验: 类的继承,super
Account
package com.account.java;
public class Account {
private int id; //账号
private double balance; //余额
private 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 annualInterestRate / 12;
}
//取钱
public void withdraw (double amount){
if(balance >= amount){
balance -= amount;
return;
}
System.out.println("余额不足");
}
//存钱
public void deposit (double amount){
if(amount > 0){
balance += amount;
}
}
}
AccountTest
package com.account.java;
/*
* 写一个用户程序测试 Account 类。在用户程序中,创建一个账号为 1122、余额为 20000、
年利率 4.5%的 Account 对象。使用 withdraw 方法提款 30000 元,并打印余额。
再使用 withdraw 方法提款 2500 元,使用 deposit 方法存款 3000 元,然后打印余额和月利
率。
*/
public class AccountTest {
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);
System.out.println("你的余额为: " + account.getBalance());
account.deposit(3000);
System.out.println("你的余额为: " + account.getBalance());
System.out.println("月利率为: " + (account.getMonthlyInterest()*100) + "%");
}
}
CheckAccount
package com.account.java;
/*
* 创建 Account 类的一个子类 CheckAccount 代表可透支的账户,该账户中定义一个属性
overdraft 代表可透支限额。在 CheckAccount 类中重写 withdraw 方法,其算法如下:
如果(取款金额<账户余额),
可直接取款
如果(取款金额>账户余额),
计算需要透支的额度
判断可透支额 overdraft 是否足够支付本次透支需要,如果可以
将账户余额修改为 0,冲减可透支金额
如果不可以
提示用户超过可透支额的限额
*
*
*/
public class CheckAccount extends Account{
private double overdraft; // 可透支限额
public double getOverdraft() {
return overdraft;
}
public void setOverdraft(double overdraft) {
this.overdraft = overdraft;
}
public CheckAccount(int id, double balance, double annualInterestRate,double overdraft){
super(id, balance, annualInterestRate);
this.overdraft = overdraft;
}
@Override
public void withdraw(double amount) {
if(getBalance() >= amount){ //余额足够
// //方式一
// setBalance(getBalance() - amount);
//方式二
super.withdraw(amount);
}else if(overdraft >= amount - getBalance()){ //透支额度足够
overdraft -= (amount - getBalance());
// setBalance(0);
super.withdraw(getBalance());
}else {
System.out.println("超过可透支额度!....");
}
}
}
CheckAccountTest
package com.account.java;
/*
* 写一个用户程序测试 CheckAccount 类。在用户程序中,创建一个账号为 1122、余额为 20000、年利率 4.5%,可透支限额为 5000 元的 CheckAccount 对象。
使用 withdraw 方法提款 5000 元,并打印账户余额和可透支额。
再使用 withdraw 方法提款 18000 元,并打印账户余额和可透支额。
再使用 withdraw 方法提款 3000 元,并打印账户余额和可透支额。
*/
public class CheckAccountTest {
public static void main(String[] args) {
CheckAccount acct = new CheckAccount(1122, 20000, 0.045, 5000);
acct.withdraw(5000);
System.out.println("你的余额为:" + acct.getBalance());
System.out.println("你的可透支额度为:" + acct.getOverdraft());
acct.withdraw(18000);
System.out.println("你的余额为:" + acct.getBalance());
System.out.println("你的可透支额度为:" + acct.getOverdraft());
acct.withdraw(3000);
System.out.println("你的余额为:" + acct.getBalance());
System.out.println("你的可透支额度为:" + acct.getOverdraft());
}
}
七、面向对象特征之三: 多态性
1.概念
多态性,是面向对象中最重要的概念,在Java中的体现:
对象的多态性:父类的引用指向子类的对象
可以直接应用在抽象类和接口上
Java引用变量有两个类型:编译时类型和运行时类型。编译时类型由声明该变量时使用的类型决定,运行时类型由实际赋给该变量的对象决定。简称:编译时,看左边;运行时,看右边。
若编译时类型和运行时类型不一致,就出现了对象的多态性(Polymorphism)
多态情况下, “看左边” :看的是父类的引用(父类中不具备子类特有的方法) “看右边” :看的是子类的对象(实际运行的是子类重写父类的方法)
对象的多态 —在Java中,子类的对象可以替代父类的对象使用
一个变量只能有一种确定的数据类型
一个引用类型变量可能指向(引用)多种不同类型的对象
子类可看做是特殊的父类,所以父类类型的引用可以指向子类的对象:向 上转型(upcasting)。
一个引用类型变量如果声明为父类的类型,但实际引用的是子类 对象,那么该变量就不能再访问子类中添加的属性和方法
Student m = new Student(); m.school = “pku”; //合法,Student类有school成员变量
Person e = new Student(); e.school = “pku”; //非法,Person类没有school成员变量
属性是在编译时确定的,编译时e为Person类型,没有school成员变量,因而编译错误。
2.虚拟方法调用(Virtual Method Invocation)
正常的方法调用
Person e = new Person();
e.getInfo();
Student e = new Student();
e.getInfo();
虚拟方法调用(多态情况下)
子类中定义了与父类同名同参数的方法,在多态情况下,将此时父类的方法称为虚拟方法,父 类根据赋给它的不同子类对象,动态调用属于子类的该方法。这样的方法调用在编译期是无法 确定的。
Person e = new Student();
e.getInfo(); //调用Student类的getInfo()方法
编译时类型和运行时类型
编译时e为Person类型,而方法的调用是在运行时确定的,所以调用的是Student类 的getInfo()方法。——动态绑定
3.小结:方法的重载与重写
1. 二者的定义细节:重写:在子类中可以根据需要对从父类中继承来的方法进行改造,也称 为方法的重置、覆盖。在程序执行时,子类的方法将覆盖父类的方法。
重载:方法和构造器的重载—— "两同一不同”:同一个类、相同方法名 参数列表不同:参数个数不同,参数类型不同
在同一个类中,允许存在一个以上的同名方法,只要它们的参数个数或者参数 类型不同即可。
2. 从编译和运行的角度看:
重载,是指允许存在多个同名方法,而这些方法的参数不同。编译器根据方法不 同的参数表,对同名方法的名称做修饰。对于编译器而言,这些同名方法就成了 不同的方法。它们的调用地址在编译期就绑定了。Java的重载是可以包括父类 和子类的,即子类可以重载父类的同名不同参数的方法。
所以:对于重载而言,在方法调用之前,编译器就已经确定了所要调用的方法, 这称为“早绑定”或“静态绑定”;
而对于多态,只有等到方法调用的那一刻,解释运行器才会确定所要调用的具体 方法,这称为“晚绑定”或“动态绑定”。
引用一句Bruce Eckel的话:“不要犯傻,如果它不是晚绑定,它就不是多态。”
4.代码演示
Person父类
package com.tyl2.java;
public class Person {
String name;
int age;
int id = 1001;
public void eat(){
System.out.println("吃饭");
}
public void walk(){
System.out.println("走路");
}
}
Man子类
package com.tyl2.java;
public class Man extends Person{
boolean isSmoking;
int id = 1002;
public void earnMoney(){
System.out.println("男的挣钱");
}
@Override
public void eat(){
System.out.println("男人多吃肉");
}
@Override
public void walk(){
System.out.println("男人霸气走路");
}
}
Woman子类
package com.tyl2.java;
public class Moman extends Person{
boolean isBeauty;
public void goShopping(){
System.out.println("女人喜欢购物");
}
@Override
public void eat(){
System.out.println("女人少吃");
}
@Override
public void walk(){
System.out.println("苗条走路");
}
}
PersonTest测试类
package com.tyl2.java;
/*
* 面向对象特征之三:多态性
*
* 1.理解多态性:可以理解为一个事物的多种形态。
2.何为多态性:
对象的多态性:父类的引用指向子类的对象
* 3.多态的使用:虚拟方法调用
有了对象的多态性以后,我们在编译期,只能调用父类中声明的方法,但在运行期,我们实际执行的是子类重写父类的方法。
总结:编译,看左边;运行,看右边。
* 4.多态性的使用前提:①类的继承关系 ②方法的重写
*
* 5.对象的多态性,只适用于方法,不适用于属性(编译和运行都看左边)
*
*/
public class PersonTest {
public static void main(String[] args) {
Person p1 = new Person();
p1.eat();
Man man = new Man();
man.eat();
man.age = 25;
man.earnMoney();
System.out.println(".................................");
//....................................................
// 对象的多态性, 父类的引用指向子类的对象
Person p2 = new Man();
// Person p3 = new Moman();
// 多态的使用: 当调用子父类同名同参数的方法时,实际执行的是子类重写父类的方法~~~虚拟方法调用
p2.eat();
p2.walk();
// p2.earnMoney();
System.out.println(p2.id); //1001
}
}
5.举例
package com.tyl2.java;
/*
* 多态性的使用举例一:
*
*
*
*/
import java.sql.Connection;
public class AnimalTest {
public static void main(String[] args) {
AnimalTest test = new AnimalTest();
test.func(new Dog());
test.func(new Cat());
}
public void func(Animal animal){ // Animal animal = new Dog();
animal.eat();
animal.shout();
}
// public void func(Dog dog){
// dog.eat();
// dog.shout();
// }
//
// public void func(Cat cat){
// cat.eat();
// cat.shout();
// }
}
class Animal{
public void eat(){
System.out.println("动物进食");
}
public void shout(){
System.out.println("动物, 叫");
}
}
class Dog extends Animal{
public void eat(){
System.out.println("狗吃骨头");
}
public void shout(){
System.out.println("汪汪汪!!!");
}
}
class Cat extends Animal{
public void eat(){
System.out.println("猫吃鱼");
}
public void shout(){
System.out.println("喵喵喵!!!");
}
}
//举例二:
class Order{
public void method(Object object){
}
}
// 举例三:
class Driver{
public void doData(Connection cnn){ //cnn = new MySQLConnection(); // cnn = new OracleConnection();
//规范的步骤去操作数据
// cnn.method1();
// cnn.method2();
// cnn.method3();
}
}
6.instanceof 操作符
x instanceof A:检验x是否为类A的对象,返回值为boolean型。
要求x所属的类与类A必须是子类和父类的关系,否则编译错误。
如果x属于类A的子类B,x instanceof A值也为true。
代码演示
package com.tyl.com;
public class PersonTest {
public static void main(String[] args) {
Person p2 = new Man();
//不能调用子类特有的方法和属性,p2是Person类型
// p2.earnMoney();
p2.name = "tom";
// p2.isSmoking = true;
//有了对象的多态性以后,内存中实际上是加载了子类特有的属性和方法的,但是由于变量声明为父类类型,导致
//编译时,只能调用父类中声明的属性法。子类特有的属性和方法不能调用。
//如何调用子类特有的属性和方法
Man m1 = (Man)p2; //向下转型,使用强制类型转换符
m1.earnMoney();
m1.isSmoking = true;
//使用强转时,可能出现java.lang.ClassCastException的异常
// Moman w1 = (Moman)p2;
// w1.goShopping();
/*
* instanceof关键字的使用
* instanceof A:判断对象a是否是类A的实例。如果是,返回true;如果不是,返回false。
*
* 使用情境:为了避免在向下转型时出现ClassCastException的异常,我们在向下转型之前,先
进行instanceof的判断,一旦返回true,就进行向下转型。如果返回false,不进行向下转型。
*
* 如果a instanceof A返回true,则a instanceof B地返回true
其中,类B是类A的父类。
*
*/
if(p2 instanceof Moman){
Moman w1 = (Moman)p2;
w1.goShopping();
System.out.println("...........woman..............");
}
if(p2 instanceof Man){
Man m2 = (Man)p2;
m2.earnMoney();
System.out.println("...........man..............");
}
if(p2 instanceof Person){
System.out.println("...........person..............");
}
if(p2 instanceof Object){
System.out.println("...........object..............");
}
练习:
// 问题一:编译时通过,运行时不通过
// 举例一:
// Person p3 = new Moman();
// Man m3 = (Man)p3;
// //举例二:
// Person p4 = new Person();
// Man m4 = (Man)p4;
//
// //问题二:编译通过,运行时也通过
// object obj = new Woman();
// Person p = (Person)obj;
//
// //问题三:编译不通过
// Man m5 = new Woman();
//
// String str = new Date();
//
// Object o = new Date();
// String str1 = (String)o;
}
}
7.多态性的练习1
1)、继承成员变量和继承方法的区别
package com.tyl2.com;
/*
* 子类继承父类
1.若子类重写了父类方法,就意味着子类里定义的方法彻底覆盖了父类里的
同名方法,系统将不可能把父类里的方法转移到子类中。编译看左边,运行看右边
2.对于实例变量则不存在这样的现象,即使子类里定义了与父类完全相同的
实例变量,这个实例变量依然不可能覆盖父类中定义的实例变量。编译运行都看左边
*/
class Base {
int count = 10;
public void display() {
System.out.println(this.count);
}
}
class Sub extends Base {
int count = 20;
public void display() {
System.out.println(this.count);
}
}
public class FieldMethodTest {
public static void main(String[] args) {
Sub s = new Sub();
System.out.println(s.count); // 20
s.display(); //20
Base b = s; // 多态性
// == 对于引用数据类型来说,比较的是两个引用数据类型变量的地址值是否相同
System.out.println(b == s); // true
System.out.println(b.count); // 10 都在左边 变量不适用多态性
b.display(); // 20 运行看右边
}
}
子类继承父类
若子类重写了父类方法,就意味着子类里定义的方法彻底覆盖了父类里的 同名方法,系统将不可能把父类里的方法转移到子类中。
对于实例变量则不存在这样的现象,即使子类里定义了与父类完全相同的 实例变量,这个实例变量依然不可能覆盖父类中定义的实例变量
8.多态性的练习2
package com.tyl2.com;
/*
* 建立InstanceTest 类,在类中定义方法method(Person e);
在method中:
(1)根据e的类型调用相应类的getInfo()方法。
(2)根据e的类型执行:
如果e为Person类的对象,输出:
“a person”;
如果e为Student类的对象,输出:
“a student”
“a person ”
如果e为Graduate类的对象,输出:
“a graduated student”
“a student”
“a person”
*
*/
public class InstanceTest {
public static void main(String[] args) {
InstanceTest test = new InstanceTest();
test.method(new Student());
}
public void method(Person e){
//虚拟方法调用
String info = e.getInfo();
System.out.println(info);
// //方式1
// if(e instanceof Graduate){
// System.out.println("a graduated student");
// System.out.println(" a student");
// System.out.println(" a person");
// }else if(e instanceof Student){
// System.out.println(" a student");
// System.out.println(" a person");
// }else {
// System.out.println(" a person");
// }
//方式2
if(e instanceof Graduate){
System.out.println("a graduated student");
}
if(e instanceof Student){
System.out.println("a student");
}
if(e 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 = "pku";
@Override
public String getInfo() {
return "Name: " + name + "\nage: " + age + "\nschool: " + school;
}
}
class Graduate extends Student {
public String major = "IT";
@Override
public String getInfo() {
return "Name: " + name + "\nage: " + age + "\nschool: " + school + "\nmajor:" + major;
}
}
9.多态性的练习3
GeometricObject父类
package com.tyl3.com;
public class GeometricObject {
protected String color;
protected double 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 GeometricObject(String color, double weight) {
super();
this.color = color;
this.weight = weight;
}
public double findArea(){
return 0.0;
}
}
Circle子类和MyRectangle子类
package com.tyl3.com;
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;
}
@Override
public double findArea() {
return 3.14 * radius *radius;
}
}
package com.tyl3.com;
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;
}
@Override
public double findArea() {
return width * height;
}
}
GeomercTest测试类
package com.tyl3.com;
/*
定义一个测试类GeometricTest,编写equalsArea方法测试两个对象的面积是否相等(注意方法的参
数类型,
利用动态绑定技术),编写displayGeometricObject方法显示对象的面积(注意方法的参
数类型,利用动态绑定技术)。
*/
public class GeomercTest {
public static void main(String[] args) {
GeomercTest test = new GeomercTest();
Circle c1 = new Circle(1.5, "white", 1.0);
test.displayGeometricObject(c1);
Circle c2 = new Circle(3.5, "white", 1.0);
test.displayGeometricObject(c2);
boolean isEqual = test.equalsArea(c1, c2);
System.out.println("两个面积是否相等: " + isEqual);
MyRectangle rect = new MyRectangle(2.1, 2.1, "red", 2.0);
test.displayGeometricObject(rect);
}
public void displayGeometricObject(GeometricObject o){
System.out.println("面积为: " + o.findArea());
}
//测试两个对象的面积是否相等
public boolean equalsArea(GeometricObject o1, GeometricObject o2){
return o1.findArea() == o2.findArea();
}
}
10.多态性的面试题
package com.tyl4.com;
import java.util.Random;
//面试题:多态是编译时行为还是运行时行为? 是运行时行为
//证明如下:
class Animal {
protected void eat() {
System.out.println("animal eat food");
}
}
class Cat extends Animal {
protected void eat() {
System.out.println("cat eat fish");
}
}
class Dog extends Animal {
public void eat() {
System.out.println("Dog eat bone");
}
}
class Sheep extends Animal {
public void eat() {
System.out.println("Sheep eat grass");
}
}
public class InterviewTest {
public static Animal getInstance(int key) {
switch (key) {
case 0:
return new Cat ();
case 1:
return new Dog ();
default:
return new Sheep ();
}
}
public static void main(String[] args) {
int key = new Random().nextInt(3);
System.out.println(key);
Animal animal = getInstance(key);
animal.eat();
}
}
下面这个题有难度,好好理解一下!
package com.tyl4.com;
//考查多态的笔试题目:
public class InterviewTest1 {
public static void main(String[] args) {
Base base = new Sub();
base.add(1, 2, 3); //sub_1 视为重写
Sub s = (Sub)base; //向下转型
s.add(1,2,3); //sub_2 强转之后,优先匹配固定参数的
}
}
class Base {
public void add(int a, int... arr) {
System.out.println("base");
}
}
class Sub extends Base {
public void add(int a, int[] arr) {
System.out.println("sub_1");
}
public void add(int a, int b, int c) {
System.out.println("sub_2");
}
}
八、Object类的使用
1.概念
Object类是所有Java类的根父类
如果在类的声明中未使用extends关键字指明其父类,则默认父类 为java.lang.Object类
2.代码演示
package com.tyl4.com;
/*
* java.lang.Object.类
1.Object类是所有JaVa类的根父类
2.如果在类的声明中未使用extends关键字指明其父类,则默认父类为java.lang.Object类
3.Object类中的功能(属性、方法)就具有通用性。
4.Object类只声明了一个空参的构造器
属性:无
equals()/tostring()/getclass()/hashcode()/clone()/finalize()
wait()、notify()、notifyA1l()
*
* 面试题:
fina1、final1y、finalize的☒别?
*
*
*/
public class ObjectTest {
public static void main(String[] args) {
Order order = new Order();
System.out.println(order.getClass().getSuperclass());
}
}
class Order{
}
3.面试题:==和equals()区别
一、回顾==的使用:
==:运算符
1.可以使用在基本数据类型变量和引用数据类型变量中
2.如果比较的是基本数据类型变量:比较两个变量保存的数据是否相等。(不一定类型要相同)
如果比较的是引用数据类型变量:比较两个对象的地址值是否相同。即两个引用是否指向同一个对象实体
二、equals()法:
1.是一个方法,而非运算符
2.只能适用于引用数据类型
3.Object类中equals()的定义:
public boolean equals(object obj){
return (this =obj);
}
说明:Object类中定义的equals()和==的作用是相同的:比较两个对象的地址值是否相同。即两个引用是否指向同一个对象实体
4.像string、Date、File、包装类等都重写了object类中的equals()方法。重写以后,比较的不是
两个引用的地址是否相同,而是比较两个对象的”实体内容”是否相同。
5. 通常情况下,我们自定义的类如果使用equ1s()的话,也通常是比较两个对象的"实体内容"是否相同。那么,
就需要对object类中的equals()进行重写.
重写的原则:比较两个对象的实体内容是否相同,
package com.tyl4.com;
import java.awt.TexturePaint;
public class Customer {
String name;
int age;
public Customer() {
super();
}
public Customer(String name, int age) {
super();
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
//自动生成的
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Customer other = (Customer) obj;
if (age != other.age)
return false;
if (name == null) {
if (other.name != null)
return false;
} else if (!name.equals(other.name))
return false;
return true;
}
// //比较两个对象的实体内容是否相同
// //手动实现
// @Override
// public boolean equals(Object obj) {
//
// System.out.println("customer equals().......");
// if (this == obj) {
// return true;
// }
//
// if(obj instanceof Customer){
// Customer cust = (Customer)obj;
// //比较两个对象的每个属性都相同
if(this.age == cust.age && this.name.equals(cust.name)){
return true;
}else{
return false;
}
//
// //或者
// return this.age == cust.age && this.name.equals(cust.name);
// }
// return false;
//
// }
}
package com.tyl4.com;
import java.util.Date;
/*
*
* 面试题:==和equals()区别
一、回顾==的使用:
==:运算符
1.可以使用在基本数据类型变量和引用数据类型变量中
2.如果比较的是基本数据类型变量:比较两个变量保存的数据是否相等。(不一定类型要相同)
如果比较的是引用数据类型变量:比较两个对象的地址值是否相同。即两个引用是否指向同一个对象实体
* ニ、equals()法:
1.是一个方法,而非运算符
2.只能适用于引用数据类型
3.Object类中equals()的定义:
public boolean equals(object obj){
return (this =obj);
}
说明:Object类中定义的equals()和==的作用是相同的:比较两个对象的地址值是否相同。即两个引用是否指向同一个对象实体
*
* 4.像string、Date、File、包装类等都重写了object类中的equals()方法。重写以后,比较的不是
两个引用的地址是否相同,而是比较两个对象的”实体内容”是否相同。
5. 通常情况下,我们自定义的类如果使用equ1s()的话,也通常是比较两个对象的"实体内容"是否相同。那么,
就需要对object类中的equals()进行重写.
重写的原则:比较两个对象的实体内容是否相同,
*
*
*
*/
public class equalsTest {
public static void main(String[] args) {
//基本数据类型
int i = 10;
int j = 10;
double d = 10.0;
System.out.println(i == j); // true
System.out.println(i == d); // true
boolean b = true;
// System.out.println(i == b); //除了boolean
char c = 10;
System.out.println(i == c); // true
char c1 = 'A';
char c2 = 65;
System.out.println(c1 == c2); // true
//引用类型
Customer cust1 = new Customer("tom", 21);
Customer cust2 = new Customer("tom", 21);
System.out.println(cust1 == cust2); // false
String s1 = new String("123");
String s2 = new String("123");
System.out.println(s1 == s2); // false
System.out.println(".............................................");
System.out.println(cust1.equals(cust2)); //false -重写后为 ture
System.out.println(s1.equals(s2)); //true
Date d1 = new Date(25135435434L);
Date d2 = new Date(25135435434L);
System.out.println(d1 == d2); //false
System.out.println(d1.equals(d2)); //true
}
}
4.练习
package com.tyl5.com;
/*
* 请根据以下代码自行定义能满足需要的MyDate类,在MyDate类中覆盖
equals方法,使其判断当两个MyDate类型对象的年月日都相同时,结果
为true,否则为false。 public boolean equals(Object o)
*
*/
public class MyDateTest {
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;
}
// @Override
// public boolean equals(Object obj) {
// if (this == obj)
// return true;
// if (obj == null)
// return false;
// if (getClass() != obj.getClass())
// return false;
// MyDate other = (MyDate) obj;
// if (day != other.day)
// return false;
// if (month != other.month)
// return false;
// if (year != other.year)
// return false;
// return true;
// }
@Override
public boolean equals(Object obj) {
if(this == obj){
return true;
}
if(obj instanceof MyDate){
MyDate myDate = (MyDate)obj;
return this.day == myDate.day && this.month == myDate.month && this.year == myDate.year;
}
return false;
}
}
5.toString() 方法
package com.tyl5.com;
/*
* Object类中tostring()的使用:
1.当我们输出一个对象的引用时,实际上就是调用当前对象的tostring()
2.object类中tostring()的定义:
public String toString() {
return getClass().getName() + "@" + Integer.toHexString(hashCode());
}
3.像String、Date、File、包装类等都重写了Object类中的toString()方法。
使得在调用对象的tostring()时,返回"实体内容"信息
4.自定义类也可以重写toString()方法,当调用此方法时,返回对象的"实体内容"
*/
import java.util.Date;
import com.tyl4.com.Customer;
public class ToStringTest {
public static void main(String[] args) {
Customer cust1 = new Customer("ton", 21);
System.out.println(cust1.toString()); //com.tyl4.com.Customer@15db9742 重写之后 Customer[name = ton, age = 21]
System.out.println(cust1); //com.tyl4.com.Customer@15db9742 重写之后 Customer[name = ton, age = 21]
String str = new String("mm");
System.out.println(str);
Date date = new Date(5445454564654L);
System.out.println(date.toString()); //Tue Jul 24 10:16:04 CST 2142
}
}
练习
九、包装类的使用
1.单元测试
package com.tyl7.com;
import java.util.Date;
import org.junit.Test;
/*
* Java中的JUnit单元测试
步骤:
1.选中当前工程-右键选择:build path - add Libraries - JUnit4 - 下-步
* 2.创建Java类,进行单元测试。
此时的Java类要求:@此类是oublic的②此类提供公共的无参的构造器
3.此类中声明单元测试方法。
此时的单元测试方法:方法的权限是pub1ic,没有返回值,没有形参
4.此单元测试方法上需要声明注解:@Test,并在单元测试类中导入:import org.junit.Test;
5.声脱好单元测试方法以后,就可以在方法体内测试相关的代码。
6.写完代码以后,左键双击单元测试方法名,右键:run as-JUnit Test
说明:
1,如果执行结果没有任何异常:绿条
2,如果执行结果出现异常:红条
*
*/
public class JunitTest {
int number = 10;
@Test
public void testEquals(){
String s1 = "mm";
String s2 = "mm";
System.out.println(s1.equals(s2));
//ClassCastException的异常
// Object obj = new String("GG");
// Date date = (Date)obj;
System.out.println(number);
show();
}
public void show(){
number = 20;
System.out.println("show()......");
}
@Test
public void testToString(){
String s3 = "kk";
System.out.println(s3);
}
}
2.包装类(Wrapper)的使用
3.总结:基本类型、包装类与String类间的转换
4.代码演示
package com.tyl7.com;
/*
* 包装类的使用:
1.jaVa提供了8种基本数据类型对应的包装类,使得基本数据类型的变量具有类的特征
2.掌握的:基本数据类型、包装类、String三者之间的相互转换
*
*/
import org.junit.Test;
public class WrapperTest {
//String类型--->基本数据类型、包装类 调用包装类的parseXxx(String s)
@Test
public void test5(){
//可能会报NumberFormatException
String str1 = "123";
//错误的情况:
// int num1 = (int)str1;
// Integer in1 = (Integer)str1;
int num2 = Integer.parseInt(str1);
System.out.println(num2 + 1);
String str2 = "true";
boolean b1 = Boolean.parseBoolean(str2);
System.out.println(b1);
}
// 基本数据类型、包装类--->String类型 调用string重载的value0f(XXXXXX)
@Test
public void test4(){
int num1 = 10;
//方式1:连接运算
String str1 = num1 + "";
//方式2
float f1 = 12.3f;
String str2 = String.valueOf(f1); // "12.3"
System.out.println(str2);
Double double1 = new Double(12.4);
String str3 = String.valueOf(double1);
System.out.println(str3); // "12.4"
}
/**
*
* @Description JDK5.0新特性:自动装箱与自动拆箱
* @author tyl
* @date 2023年4月13日上午11:09:42
*/
@Test
public void test3(){
// int num1 = 10;
// //基本数据类型-~>包装类的对象
// method(num1);
//自动装箱 基本数据类型··~>包装类
int num2 = 10;
Integer in1 = num2; //自动装箱
boolean b1 = true;
Boolean b2 = b1; //自动装箱
//自动拆箱 包装类-·->基本数据类型
System.out.println(in1.toString());
int num3 = in1;
}
public void method(Object obj){
}
//包装类--->基本数据类型:调用包装类的xxxValue()
@Test
public void test2(){
Integer in1 = new Integer(12);
int i1 = in1.intValue();
System.out.println(i1 + 1);
Float f1 = new Float(12.3);
float f2 = f1.floatValue();
System.out.println(f2 + 1);
}
// 基本数据类型-·->包装类 调用包装类的构造器
@Test
public void test1(){
int num1 = 10;
Integer in1 = new Integer(num1);
System.out.println(in1.toString());
Integer in2 = new Integer("123");
System.out.println(in2.toString());
//异常
// Integer in2 = new Integer("123abc");
// System.out.println(in2.toString());
Float f1 = new Float(12.3f);
Float f2 = new Float("12.3");
System.out.println(f1);
System.out.println(f2);
Boolean b1 = new Boolean(true);
Boolean b2 = new Boolean("true");
Boolean b4 = new Boolean("True");
System.out.println(b2);
System.out.println(b4);
Boolean b3 = new Boolean("true123");
System.out.println(b3); //false
Order order = new Order();
System.out.println(order.isMale); //false
System.out.println(order.isFemle); //null
}
}
class Order{
boolean isMale;
Boolean isFemle;
}
5.面试题
package com.tyl7.com;
/*
*
* 关于包装类使用的面试题
*
*
*/
import org.junit.Test;
public class InterViewTest {
@Test
public void test1() {
Object o1 = true ? new Integer(1) : new Double(2.0); // 自动类型提升
System.out.println(o1); // 1.0
}
@Test
public void test2() {
Object o2;
if (true)
o2 = new Integer(1);
else
o2 = new Double(2.0);
System.out.println(o2); // 1
}
@Test
public void test3() {
Integer i = new Integer(1);
Integer j = new Integer(1);
System.out.println(i == j); // false
//Integer内部定义了IntegerCache结构,IntegerCache中定义了Integer【】,
//保存了从~128~127范围的整数。如果我们使用自动装箱的方式,给Integer赋值的范围在
//-128~127范围内时,可以直接使用数组中的元素,不用再去new了。目的:提高效率
Integer m = 1;
Integer n = 1;
System.out.println(m == n); //true
Integer x = 128; //相当于neW了一个Integer对象
Integer y = 128;
System.out.println(x == y); // false
}
}
6.练习
package com.tyl7.com;
/*
* 利用Vector代替数组处理:从键盘读入学生成绩(以负数代表输入结束),找出
最高分,并输出学生成绩等级。
提示:数组一旦创建,长度就固定不变,所以在创建数组前就需要知道它的
长度。而向量类java.util.Vector可以根据需要动态伸缩。
创建Vector对象:Vector v=new Vector();
给向量添加元素:v.addElement(Object obj); //obj必须是对象
取出向量中的元素:Object obj=v.elementAt(0);
注意第一个元素的下标是0,返回值是Object类型的。
计算向量的长度:v.size();
若与最高分相差10分内:A等;20分内:B等;
30分内:C等;其它:D等
*
*
*
*
*/
import java.util.Scanner;
import java.util.Vector;
import java.util.logging.Level;
public class ScoreTest {
public static void main(String[] args) {
//1.实例化Scanner,用于从键盘获取学生成绩
Scanner scan = new Scanner(System.in);
//2.创建Vector对象:Vector v=new Vector();
Vector v = new Vector();
//3.for(;;)或者while(true)方式,给Vector添加数据
int maxScore = 0;
for(;;){
System.out.println("请输入学生成绩(以负数代表输入结束)");
int score = scan.nextInt();
//3.2.当输入是负数时,跳出循环
if(score < 0){
break;
}
if(score > 100){
System.out.println("输入的数据非法,请重新输入");
continue;
}
//3.1. 给向量添加元素:v.addElement(Object obj); //obj必须是对象
// //jdk5.0之前
// Integer inScore = new Integer(score);
// v.addElement(inScore); //多态
//jdk5.0之后
v.addElement(score); //自动装箱
//4.获取学生成绩的最大值
if(maxScore < score){
maxScore = score;
}
}
//5.遍历Vector,得到每个学生的成绩,并与最大成绩比较,得到每个学生的等级。
char level;
for (int i =0; i < v.size(); i++){
Object obj = v.elementAt(i);
//jdk5.0之前
// Integer inScore = (Integer)obj;
// int score = inScore.intValue();
//jdk5.0之后
int score = (int)obj;
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("student-" + i + "score is" + score + ", level is" + level);
}
}
}
7.谈谈你对多态性的理解?
感觉这章还挺难的,得好好复习,加油吧年轻人。