文章目录
前言
面向对象编程(中)
一、面向对象的特征之二:继承性
一、继承性的好处:
① 减少了代码的冗余,提高了代码的复用性
② 便于功能的扩展
③ 为之后多态性的使用,提供了前提
二、继承性的格式:
class A extends B{}
A:子类、派生类、subclass
B:父类、超类、基类、superclass
2.1体现:一旦子类A继承父类B以后,子类A中就获取了父类B中声明的所有的属性和方法。
特别的,父类中声明为private的属性或方法,子类继承父类以后,仍然认为获取了父类中私有的结构。
只有因为封装性的影响,使得子类不能直接调用父类的结构而已。
2.2 子类继承父类以后,还可以声明自己特有的属性或方法:实现功能的拓展。
子类和父类的关系,不同于子集和集合的关系。
extends:延展、扩展
三、Java中关于继承性的规定:
1.一个类可以被多个子类继承。
2.Java中类的单继承性:一个类只能有一个父类
3.子父类是相对的概念。
4.子类直接继承的父类,称为:直接父类。间接继承的父类称为:间接父类
5.子类继承父类以后,就获取了直接父类以及所有间接父类中声明的属性和方法
四、
1. 如果我们没有显式的声明一个类的父类的话,则此类继承于java.lang.Object类
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 = 1;
p1.eat();
System.out.println("*****************");
Student s1 = new Student();
s1.eat();
// s1.sleep();
s1.name = "Tom";
s1.setAge(10);
System.out.println(s1.getAge());
s1.breath();
Creature c = new Creature();
System.out.println(c.toString());
}
}
public class Person extends Creature{
String name;
private int age;
public Person(){
}
public Person(String name,int age){
this.name = name;
this.age = age;
}
public void eat(){
System.out.println("吃饭");
sleep();
}
private void sleep(){
System.out.println("睡觉");
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
package com.atguigu.java;
public class Student extends Person{
// String name;
// int age;
String major;
public Student(){
}
public Student(String name,int age,String major){
this.name = name;
// this.age = age;
setAge(age);
this.major = major;
}
// public void eat(){
// System.out.println("吃饭");
// }
//
// public void sleep(){
// System.out.println("睡觉");
// }
public void study(){
System.out.println("学习");
}
public void show(){
System.out.println("name:" + name + ",age:" + getAge());
}
}
package com.atguigu.java;
public class Creature {
public void breath(){
System.out.println("呼吸");
}
}
继承性的练习题
1、定义一个ManKind类,包括
成员变量int sex和int salary;
方法void manOrWoman():根据sex的值显示“man”(sex1)或者“woman”(sex0);
方法void employeed():根据salary的值显示“no job”(salary==0)或者“ job”(salary!=0)。
2、定义类Kids继承ManKind,并包括
成员变量int yearsOld;
方法printAge()打印yearsOld的值。
3、定义类KidsTest,在类的main方法中实例化Kids的对象someKid,用该对象访问其父类的成员变量及方法。
public class ManKind {
private int sex;//性别
private int salary;//薪资
public ManKind() {
}
public ManKind(int sex, int salary) {
this.sex = sex;
this.salary = salary;
}
public void manOrWoman(){
if(sex == 1){
System.out.println("man");
}else if(sex == 0){
System.out.println("woman");
}
}
public void employeed(){
// if(salary == 0){
// System.out.println("no job");
// }else{
// System.out.println("job");
// }
//或
String jobInfo = (salary == 0)? "no job" : "job";
System.out.println(jobInfo);
}
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 class Kids extends ManKind{
private int yearsOld;
public Kids() {
}
public Kids(int yearsOld) {
this.yearsOld = yearsOld;
}
public void printAge(){
System.out.println("I am " + yearsOld + " years old.");
}
public int getYearsOld() {
return yearsOld;
}
public void setYearsOld(int yearsOld) {
this.yearsOld = yearsOld;
}
/*
* 修改练习1.2中定义的类Kids,在Kids中重新定义employeed()方法,
* 覆盖父类ManKind中定义的employeed()方法,
* 输出“Kids should study and no job.”
*/
public void employeed() {
System.out.println("Kids should study and no job.");
}
}
public class KidsTest {
public static void main(String[] args) {
Kids someKid = new Kids(12);
someKid.printAge();
someKid.setSalary(0);
someKid.setSex(1);
someKid.employeed();
someKid.manOrWoman();
}
}
二、方法的重写
方法的重写(override / overwrite)
1.重写:子类继承父类以后,可以对父类中同名同参数的方法,进行覆盖操作
2.应用:重写以后,当创建子类对象以后,通过子类对象调用子父类中的同名同参数的方法时,实际执行的是子类重写父类的方法。
- 重写的规定:
方法的声明: 权限修饰符 返回值类型 方法名(形参列表) throws 异常的类型{
//方法体
}
约定俗称:子类中的叫重写的方法,父类中的叫被重写的方法
① 子类重写的方法的方法名和形参列表与父类被重写的方法的方法名和形参列表相同
② 子类重写的方法的权限修饰符不小于父类被重写的方法的权限修饰符
>特殊情况:子类不能重写父类中声明为private权限的方法
③ 返回值类型:
>父类被重写的方法的返回值类型是void,则子类重写的方法的返回值类型只能是void
>父类被重写的方法的返回值类型是A类型,则子类重写的方法的返回值类型可以是A类或A类的子类
>父类被重写的方法的返回值类型是基本数据类型(比如:double),则子类重写的方法的返回值类型必须是相同的基本数据类型(必须也是double)
④ 子类重写的方法抛出的异常类型不大于父类被重写的方法抛出的异常类型(具体放到异常处理时候讲)
子类和父类中的同名同参数的方法要么都声明为非static的(考虑重写),要么都声明为static的(不是重写)。
面试题:区分方法的重载与重写
答:重写是子类对父类的允许访问的方法的实现过程进行重新编写, 返回值和形参都不能改变。即外壳不变,核心重写!
重载(overloading) 是在一个类里面,方法名字相同,而参数不同。返回类型可以相同也可以不同。
代码示例:
public class PersonTest {
public static void main(String[] args) {
Student s = new Student("计算机科学与技术");
s.eat();
s.walk(10);
System.out.println("**************");
s.study();
Person p1 = new Person();
p1.eat();
}
}
public class Student extends Person{
String major;
public Student(){
}
public Student(String major){
this.major = major;
}
public void study(){
System.out.println("学习。专业是:" + major);
}
//对父类中的eat()进行了重写
public void eat(){
System.out.println("学生应该多吃有营养的食物");
}
public void show(){
System.out.println("我是一个学生");
}
public String info(){
return null;
}
// public int info1(){
// return 1;
// }
// public void walk(int distance){
// System.out.println("重写的方法");
// }
public void walk(int distance) {
System.out.println("重写的方法");
}
}
public class Person {
String name;
int age;
public Person(){
}
public Person(String name,int age){
this.name = name;
this.age = age;
}
void eat(){
System.out.println("吃饭");
}
public void walk(int distance){
System.out.println("走路,走的距离是:" + distance + "公里");
show();
eat();
}
private void show(){
System.out.println("我是一个人");
}
public Object info(){
return null;
}
public double info1(){
return 1.0;
}
}
三、四种访问权限修饰符
private只能在本类中使用,default本包的可用,protected不同包的子类可以使用,public不同包也能使用。
四、super关键字
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(形参列表)",
则默认调用的是父类中空参的构造器:super()
4.5 在类的多个构造器中,至少有一个类的构造器中使用了"super(形参列表)",调用父类中的构造器
代码示例:
package com.atguigu.java3;
public class Person {
String name;
int age;
int id = 1001;//身份证号
public Person(){
System.out.println("我无处不在!");
}
public Person(String name){
this.name = name;
}
public Person(String name,int age){
this(name);
this.age = age;
}
public void eat(){
System.out.println("人:吃饭");
}
public void walk(){
System.out.println("人:走路");
}
}
public class Student extends Person{
String major;
int id = 1002;//学号
public Student(){
super();
}
public Student(String major){
super();
this.major = major;
}
public Student(String name,int age,String major){
// this.name = name;
// this.age = age;
super(name,age);
this.major = major;
}
@Override
public void eat() {
System.out.println("学生:多吃有营养的食物");
}
public void study(){
System.out.println("学生:学习知识");
this.eat();
super.eat();
walk();
}
public void show(){
System.out.println("name = " + name + ", age = " + age);
System.out.println("id = " + this.id);
System.out.println("id = " + super.id);
}
}
public class SuperTest {
public static void main(String[] args) {
Student s = new Student();
s.show();
System.out.println();
s.study();
Student s1 = new Student("Tom", 21, "IT");
s1.show();
System.out.println("************");
Student s2 = new Student();
}
}
super练习:
题目
/*
* 创建Account类的一个子类CheckAccount代表可透支的账户,
* 该账户中定义一个属性overdraft代表可透支限额。
* 在CheckAccount类中重写withdraw方法,其算法如下:
如果(取款金额<账户余额),
可直接取款
如果(取款金额>账户余额),
计算需要透支的额度
判断可透支额overdraft是否足够支付本次透支需要,如果可以
将账户余额修改为0,冲减可透支金额
如果不可以
提示用户超过可透支额的限额
*/
* 写一个用户程序测试CheckAccount类。
* 在用户程序中,创建一个账号为1122、余额为20000、
* 年利率4.5%,可透支限额为5000元的CheckAccount对象。
使用withdraw方法提款5000元,并打印账户余额和可透支额。
再使用withdraw方法提款18000元,并打印账户余额和可透支额。
再使用withdraw方法提款3000元,并打印账户余额和可透支额。
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;
}
}
}
/*
* 写一个用户程序测试Account类。在用户程序中,
* 创建一个账号为1122、余额为20000、年利率4.5%的Account对象。
* 使用withdraw方法提款30000元,并打印余额。
* 再使用withdraw方法提款2500元,
* 使用deposit方法存款3000元,然后打印余额和月利率。
*
*/
public class AccountTest {
public static void main(String[] args) {
Account acct = new Account(1122, 20000, 0.045);
acct.withdraw(30000);
System.out.println("您的账户余额为:" + acct.getBalance());
acct.withdraw(2500);
System.out.println("您的账户余额为:" + acct.getBalance());
acct.deposit(3000);
System.out.println("您的账户余额为:" + acct.getBalance());
System.out.println("月利率为:" + (acct.getMonthlyInterest() * 100) +"%");
}
}
/*
* 创建Account类的一个子类CheckAccount代表可透支的账户,该账户中定义一个属性overdraft代表可透支限额。
* 在CheckAccount类中重写withdraw方法,其算法如下:
如果(取款金额<账户余额),
可直接取款
如果(取款金额>账户余额),
计算需要透支的额度
判断可透支额overdraft是否足够支付本次透支需要,如果可以
将账户余额修改为0,冲减可透支金额
如果不可以
提示用户超过可透支额的限额
*
*
*/
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;
}
@Override
public void withdraw(double amount) {
if(getBalance() >= amount){//余额就足够消费
// 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("超过可透支限额!");
}
}
}
/*
* 写一个用户程序测试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());
}
}
五、子类对象实例化过程
- 从结果上来看:(继承性)
子类继承父类以后,就获取了父类中声明的属性或方法。
创建子类的对象,在堆空间中,就会加载所有父类中声明的属性。 - 从过程上来看:
当我们通过子类的构造器创建子类对象时,我们一定会直接或间接的调用其父类的构造器,进而调用父类的父类的构造器,…
直到调用了java.lang.Object类中空参的构造器为止。正因为加载过所有的父类的结构,所以才可以看到内存中有
父类中的结构,子类对象才可以考虑进行调用。
明确:虽然创建子类对象时,调用了父类的构造器,但是自始至终就创建过一个对象,即为new的子类对象。
六、面向对象的特征之三:多态性
1.理解多态性:可以理解为一个事物的多种形态。
2.何为多态性:
对象的多态性:父类的引用指向子类的对象(或子类的对象赋给父类的引用)
3. 多态的使用:虚拟方法调用
有了对象的多态性以后,我们在编译期,只能调用父类中声明的方法,但在运行期,我们实际执行的是子类重写父类的方法。
总结:编译,看左边;运行,看右边。
4.多态性的使用前提: ① 类的继承关系 ② 方法的重写
5.对象的多态性,只适用于方法,不适用于属性(编译和运行都看左边)
6.多态是运行时的行为。
代码示例:
/*
* 面向对象特征之三:多态性
*
* 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 Woman();
//多态的使用:当调用子父类同名同参数的方法时,实际执行的是子类重写父类的方法 ---虚拟方法调用
p2.eat();
p2.walk();
// p2.earnMoney();
System.out.println(p2.id);//1001
}
}
public class Person {
String name;
int age;
int id = 1001;
public void eat(){
System.out.println("人:吃饭");
}
public void walk(){
System.out.println("人:走路");
}
}
public class Man extends Person{
boolean isSmoking;
int id = 1002;
public void earnMoney(){
System.out.println("男人负责挣钱养家");
}
public void eat(){
System.out.println("男人多吃肉,长肌肉");
}
public void walk(){
System.out.println("男人霸气的走路");
}
}
public class Woman extends Person{
boolean isBeauty;
public void goShopping(){
System.out.println("女人喜欢购物");
}
public void eat(){
System.out.println("女人少吃,为了减肥");
}
public void walk(){
System.out.println("女人窈窕的走路");
}
}
多态性的 用处:
package com.atguigu.java4;
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();
if(animal instanceof Dog){
Dog d = (Dog)animal;
d.watchDoor();
}
}
// 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("汪!汪!汪!");
}
public void watchDoor(){
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 obj){
}
}
//举例三:
class Driver{
public void doData(Connection conn){//conn = new MySQlConnection(); / conn = new OracleConnection();
//规范的步骤去操作数据
// conn.method1();
// conn.method2();
// conn.method3();
}
}
1、 instanceof操作符
instanceof关键字的使用
a instanceof A:判断对象a是否是类A的实例。如果是,返回true;如果不是,返回false。
* 使用情境:为了避免在向下转型时出现ClassCastException的异常,我们在向下转型之前,先
* 进行instanceof的判断,一旦返回true,就进行向下转型。如果返回false,不进行向下转型。
*
* 如果 a instanceof A返回true,则 a instanceof B也返回true.
* 其中,类B是类A的父类。
代码示例:
package com.atguigu.java;
import java.util.Date;
/*
* 面向对象特征之三:多态性
*
* 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 Woman();
//多态的使用:当调用子父类同名同参数的方法时,实际执行的是子类重写父类的方法 ---虚拟方法调用
p2.eat();
p2.walk();
// p2.earnMoney();
System.out.println(p2.id);//1001
System.out.println("****************************");
//不能调用子类所特有的方法、属性:编译时,p2是Person类型。
p2.name = "Tom";
// p2.earnMoney();
// p2.isSmoking = true;
//有了对象的多态性以后,内存中实际上是加载了子类特有的属性和方法的,但是由于变量声明为父类类型,导致
//编译时,只能调用父类中声明的属性和方法。子类特有的属性和方法不能调用。
//如何才能调用子类特有的属性和方法?
//向下转型:使用强制类型转换符。
Man m1 = (Man)p2;
m1.earnMoney();
m1.isSmoking = true;
//使用强转时,可能出现ClassCastException的异常。
// Woman w1 = (Woman)p2;
// w1.goShopping();
/*
* instanceof关键字的使用
*
* a instanceof A:判断对象a是否是类A的实例。如果是,返回true;如果不是,返回false。
*
*
* 使用情境:为了避免在向下转型时出现ClassCastException的异常,我们在向下转型之前,先
* 进行instanceof的判断,一旦返回true,就进行向下转型。如果返回false,不进行向下转型。
*
* 如果 a instanceof A返回true,则 a instanceof B也返回true.
* 其中,类B是类A的父类。
*/
if(p2 instanceof Woman){
Woman w1 = (Woman)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******");
}
// if(p2 instanceof String){
//
// }
//练习:
//问题一:编译时通过,运行时不通过
//举例一:
// Person p3 = new Woman();
// 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;
}
}
//class Order{
//
//}
2、多态性的练习(这个很重要,要看懂)
练习1:
1.若子类重写了父类方法,就意味着子类里定义的方法彻底覆盖了父类里的同名方法,
系统将不可能把父类里的方法转移到子类中:编译看左边,运行看右边
2.对于实例变量则不存在这样的现象,即使子类里定义了与父类完全相同的实例变量,
这个实例变量依然不可能覆盖父类中定义的实例变量:编译运行都看左边
/*
* 练习:
* 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
}
}
练习2:
package com.atguigu.exer1;
public class GeometricObject {//几何图形
protected String color;
protected double weight;
public GeometricObject(String color, double weight) {
super();
this.color = color;
this.weight = weight;
}
public double findArea(){
return 0.0;
}
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;
}
}
package com.atguigu.exer1;
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;
}
}
package com.atguigu.exer1;
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 3.14 * radius * radius;
}
}
package com.atguigu.exer1;
/*
*
* 定义一个测试类GeometricTest,
* 编写equalsArea方法测试两个对象的面积是否相等(注意方法的参数类型,利用动态绑定技术),
* 编写displayGeometricObject方法显示对象的面积(注意方法的参数类型,利用动态绑定技术)。
*/
public class GeometricTest {
public static void main(String[] args) {
GeometricTest test = new GeometricTest();
Circle c1 = new Circle(3.3, "white", 1.0);
test.displayGeometricObject(c1);
Circle c2 = new Circle(3.3, "white", 1.0);
test.displayGeometricObject(c2);
boolean isEquals = test.equalsArea(c1, c2);
System.out.println("c1 和 c2的面积是否相等:" + isEquals);
MyRectangle rect = new MyRectangle(2.1, 3.4, "red", 2.0);
test.displayGeometricObject(rect);
}
public void displayGeometricObject(GeometricObject o){//GeometricObject o = new Circle(...)
System.out.println("面积为:" + o.findArea());
}
//测试两个对象的面积是否相等
public boolean equalsArea(GeometricObject o1,GeometricObject o2){
return o1.findArea() == o2.findArea();
}
}
练习三:******很重要。p293
package com.atguigu.exer;
//考查多态的笔试题目:
public class InterviewTest1 {
public static void main(String[] args) {
Base1 base = new Sub1();
base.add(1, 2, 3);
Sub1 s = (Sub1)base;
s.add(1,2,3);
}
}
class Base1 {
public void add(int a, int... arr) {
System.out.println("base1");
}
}
class Sub1 extends Base1 {
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");
}
}
3、多态的总结:
七、Object类的使用
1、equals()
/*
*
* 面试题: == 和 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. 通常情况下,我们自定义的类如果使用equals()的话,也通常是比较两个对象的"实体内容"是否相同。
* 那么,我们就需要对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);
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 str1 = new String("atguigu");
String str2 = new String("atguigu");
System.out.println(str1 == str2);//false
System.out.println("****************************");
System.out.println(cust1.equals(cust2));//false--->true
System.out.println(str1.equals(str2));//true
Date date1 = new Date(32432525324L);
Date date2 = new Date(32432525324L);
System.out.println(date1.equals(date2));//true
}
}
/*
* java.lang.Object类
* 1.Object类是所有Java类的根父类
* 2.如果在类的声明中未使用extends关键字指明其父类,则默认父类为java.lang.Object类
* 3.Object类中的功能(属性、方法)就具有通用性。
* 属性:无
* 方法:equals() / toString() / getClass() /hashCode() / clone() / finalize()
* wait() 、 notify()、notifyAll()
*
* 4. Object类只声明了一个空参的构造器
*
*
*
* 面试题:
* final、finally、finalize的区别?
*
*/
练习:
编写Order类,有int型的orderId,String型的orderName, 相应的getter()和setter()方法,两个参数的构造器,
重写父类的equals()方法:public boolean equals(Object obj),并判断测试类中创建的两个对象是否相等。
package com.atguigu.exer2;
/*
* 编写Order类,有int型的orderId,String型的orderName,
* 相应的getter()和setter()方法,两个参数的构造器,
* 重写父类的equals()方法:public boolean equals(Object obj),
* 并判断测试类中创建的两个对象是否相等。
*/
public class OrderTest {
public static void main(String[] args) {
Order order1 = new Order(1001, "AA");
Order order2 = new Order(1001, new String("BB"));
System.out.println(order1.equals(order2));
Order order3 = new Order(1001, "BB");
System.out.println(order2.equals(order3));
// String s1 = "BB";
// String s2 = "BB";
// System.out.println(s1 == s2);//true
}
}
class Order{
private int orderId;
private String 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 Order(int orderId, String orderName) {
super();
this.orderId = orderId;
this.orderName = orderName;
}
@Override
public boolean equals(Object obj) {
if(this == obj){
return true;
}
if(obj instanceof Order){
Order order = (Order)obj;
//正确的:
return this.orderId == order.orderId &&
this.orderName.equals(order.orderName);
//错误的:
// return this.orderId == order.orderId &&
// this.orderName == order.orderName;
}
return false;
}
}
2、toString()
/*
* 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()方法,当调用此方法时,返回对象的"实体内容"
*/
public class ToStringTest {
public static void main(String[] args) {
Customer cust1 = new Customer("Tom",21);
System.out.println(cust1.toString());//com.atguigu.java1.Customer@15db9742-->Customer[name = Tom,age = 21]
System.out.println(cust1);//com.atguigu.java1.Customer@15db9742-->Customer[name = Tom,age = 21]
String str = new String("MM");
System.out.println(str);//MM
Date date = new Date(4534534534543L);
System.out.println(date.toString());//Mon Sep 11 08:55:34 GMT+08:00 2113
}
}
八、包装类的使用
- 包装类的使用:
- 1.java提供了8种基本数据类型对应的包装类,使得基本数据类型的变量具有类的特征
- 2.掌握的:基本数据类型、包装类、String三者之间的相互转换(有自动装箱,其实只考虑两个就可以了:只记住红线画的就可)
代码示例:
public class WrapperTest {
//String类型 --->基本数据类型、包装类:调用包装类的parseXxx(String s)
@Test
public void test5(){
String str1 = "123";
//错误的情况:
// int num1 = (int)str1;
// Integer in1 = (Integer)str1;
//可能会报NumberFormatException
int num2 = Integer.parseInt(str1);
System.out.println(num2 + 1);
String str2 = "true1";
boolean b1 = Boolean.parseBoolean(str2);
System.out.println(b1);
}
//基本数据类型、包装类--->String类型:调用String重载的valueOf(Xxx xxx)
@Test
public void test4(){
int num1 = 10;
//方式1:连接运算
String str1 = num1 + "";
//方式2:调用String的valueOf(Xxx xxx)
float f1 = 12.3f;
String str2 = String.valueOf(f1);//"12.3"
Double d1 = new Double(12.4);
String str3 = String.valueOf(d1);
System.out.println(str2);
System.out.println(str3);//"12.4"
}
/*
* JDK 5.0 新特性:自动装箱 与自动拆箱
*/
@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){
System.out.println(obj);
}
//包装类--->基本数据类型:调用包装类Xxx的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;
// System.out.println(num1.toString());
Integer in1 = new Integer(num1);
System.out.println(in1.toString());
Integer in2 = new Integer("123");
System.out.println(in2.toString());
//报异常
// Integer in3 = new Integer("123abc");
// System.out.println(in3.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");
System.out.println(b2);
Boolean b3 = new Boolean("true123");
System.out.println(b3);//false
Order order = new Order();
System.out.println(order.isMale);//false
System.out.println(order.isFemale);//null
}
}
class Order{
boolean isMale;
Boolean isFemale;
}
关于包装类的一个面试题:
包装类的一个例题:
利用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等
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)
//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);
//jdk 5.0之前:
// Integer inScore = (Integer)obj;
// int score = inScore.intValue();
//jdk 5.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);
}
}
}
总结的小知识点
1、方法的重载与重写
1、二者的定义细节: 略
2. 从编译和运行的角度看:
重载,是指允许存在多个同 名方法而这些的参数不。编译器根据方法不 同的参数 表, 对同名方法的称做修饰。于编译器而言,这些就成 了 不同的方法。 它们 的调用地址在编译 期就 绑定了 。Java 的重载是可以包括父类 和子类 的,即 子类 可以重载父的同名不参数方法。
所以: 对于重载而言, 在方法调用之前编译器就已经确定了所要的这称为 “早绑定” 或静态;
而对于多态,只有等到方法调用的那一刻 ,解释运行 器才会确定所要调用的具体 方法,这称为 “晚绑定” 或动态。
重载不表现为多态性,重写表现为多态性。
引用一句 Bruce Eckel Bruce Eckel Bruce Eckel的话: “不要犯傻,如果它是 晚绑定 ,它就不是多态。”