第八天:封装、继承、抽象类
今日内容介绍
继承
抽象类
第1章继承
1.1继承的概念
在现实生活中,继承一般指的是子女继承父辈的财产。在程序中,继承描述的是事物之间的所属关系,通过继承可以使多种事物之间形成一种关系体系。例如:老师和学生都属于人的一种,程序中便可以描述老师和学生都继承自人类,这些老师和学生以会形成一个继承体系
在Java中,类的继承是指在一个现有类的基础上去构建一个新的类,构建出来的新类被称作子类,现有类被称作父类,子类会自动拥有父类所有非私有的属性和方法。
1.2继承的格式&使用
在程序中,如果想声明一个类继承另一个类,需要使用extends关键字。
格式:
class 子类 extends 父类 {}
接下来通过一个案例来学习子类是如何继承父类的,如下所示
//类中我们需要定义哪些成员变量(属性)和成员方法(行为)
public class Person {
//人的属性
String name;
//人的行为
public void sleep(){
System.out.println(name+“在睡觉”);
}
}
public class Teacher extends Person{
//老师特有的功能:教书
public void teach(){
System.out.println(name+“教Java”);//这个name是从Person中继承下来的
}
}
public class Demo {
public static void main(String[] args) {
Teacher t=new Teacher();
t.name=“老王”;//调用父类的属性
t.sleep();//调用父类的方法
t.teach();//调用自身类中的方法
}
}
1.3继承的好处&注意事项
继承的好处:
1、继承的出现提高了代码的复用性,提高软件开发效率。
2、继承的出现让类与类之间产生了关系,提供了多态的前提。
1.提高了代码的复用性
在父类中已经定义的成员变量和成员方法,在子类不用在重复定义,因为子类会把父类的非私有成员变量和成员方法继承下来,直接使用
2.继承的出现让类与类之间产生了父子关系
在类的继承中,需要注意一些问题,具体如下:
1、在Java中,类只支持单继承,不允许多继承,也就是说一个类只能有一个直接父类,例如下面这种情况是不合法的。
class A{}
class B{}
class C extends A,B{} // C类不可以同时继承A类和B类
2、多个类可以继承一个父类,例如下面这种情况是允许的。
class A{}
class B extends A{}
class C extends A{} // 类B和类C都可以继承类A
3、在Java中,多层继承是可以的,即一个类的父类可以再去继承另外的父类,例如C类继承自B类,而B类又可以去继承A类,这时,C类也可称作A类的子类。下面这种情况是允许的。
class A{}
class B extends A{} // 类B继承类A,类B是类A的子类
class C extends B{} // 类C继承类B,类C是类B的子类,同时也是类A的子类
4、在Java中,子类和父类是一种相对概念,也就是说一个类是某个类父类的同时,也可以是另一个类的子类。例如上面的这种情况中,B类是A类的子类,同时又是C类的父类。
1.4继承-子父类中成员变量的特点
了解了继承给我们带来的好处,提高了代码的复用性。继承让类与类或者说对象与对象之间产生了关系。那么,当继承出现后,类的成员之间产生了那些变化呢?
类的成员重点学习成员变量、成员方法的变化。
成员变量:如果子类父类中出现不同名的成员变量,这时的访问是没有任何问题。
看如下代码:
class Fu{
//Fu中的成员变量。
int num = 5;
}
class Zi extends Fu{
//Zi中的成员变量
int num2 = 6;
//Zi中的成员方法
public void show()
{
//访问父类中的num
System.out.println(“Fu num=”+num);
//访问子类中的num2
System.out.println(“Zi num2=”+num2);
}
}
class Demo
{
public static void main(String[] args)
{
Zi z = new Zi(); //创建子类对象
z.show(); //调用子类中的show方法
}
}
代码说明:Fu类中的成员变量是非私有的,子类中可以直接访问,若Fu类中的成员变量私有了,子类是不能直接访问的。
当子父类中出现了同名成员变量时,在子类中若要访问父类中的成员变量,必须使用关键字super来完成。Super关键字可以用来访问父类的非私有的成员。super今天不做具体讲解,在后面的课程会详细讲解
在子类中,访问父类中的成员变量格式:
super.父类中的成员变量
看如下代码:
class Fu
{
//Fu中的成员变量。
int num = 5;
}
class Zi extends Fu
{
//Zi中的成员变量
int num = 6;
void show()
{
//子父类中出现了同名的成员变量时
//在子类中需要访问父类中非私有成员变量时,需要使用super关键字
//访问父类中的num
System.out.println(“Fu num=”+super.num);
//访问子类中的num2
System.out.println(“Zi num2=”+this.num);
}
}
class Demo5
{
public static void main(String[] args)
{
Zi z = new Zi(); //创建子类对象
z.show(); //调用子类中的show方法
}
}
1.5继承-子父类中成员方法特点-重写&应用
子父类中成员方法的特点
当在程序中通过对象调用方法时,会先在子类中查找有没有对应的方法,若子类中存在就会执行子类中的方法,若子类中不存在就会执行父类中相应的方法。
看如下代码:
class Fu{
public void show(){
System.out.println(“Fu类中的show方法执行”);
}
}
class Zi extends Fu{
public void show2(){
System.out.println(“Zi类中的show2方法执行”);
}
}
public class Test{
public static void main(String[] args) {
Zi z = new Zi();
z.show(); //子类中没有show方法,但是可以找到父类方法去执行
z.show2();
}
}
成员方法特殊情况——覆盖
子类中出现与父类一模一样的方法时,会出现覆盖操作,也称为override重写、复写或者覆盖。
class Fu
{
public void show()
{
System.out.println(“Fu show”);
}
}
class Zi extends Fu
{
//子类复写了父类的show方法
public void show()
{
System.out.println(“Zi show”);
}
}
方法重写(覆盖)的应用:
当子类需要父类的功能,而功能主体子类有自己特有内容时,可以重写父类中的方法,这样,即沿袭了父类的功能,又定义了子类特有的内容。
举例:比如手机,当描述一个手机时,它具有发短信,打电话,来电显示功能,后期由于手机需要在来电显示功能中增加显示姓名和头像,这时可以重新定义一个类描述智能手机,并继承原有描述手机的类。并在新定义的类中覆盖来电显示功能,在其中增加显示姓名和头像功能。
在子类中,访问父类中的成员方法格式:
super.父类中的成员方法();
看如下代码:
public class Test {
public static void main(String[] args) {
new NewPhone().showNum();
}
}
//手机类
class Phone{
public void sendMessage(){
System.out.println(“发短信”);
}
public void call(){
System.out.println(“打电话”);
}
public void show(){
System.out.println(“来电显示号码”);
}
}
//智能手机类
class NewPhone extends Phone{
//覆盖父类的来电显示号码功能,并增加自己的显示姓名和图片功能
public void show(){
//调用父类已经存在的功能使用super
super.show();
//增加自己特有显示姓名和图片功能
System.out.println("显示来电姓名");
System.out.println("显示头像");
}
}
1.6方法重写的必备条件
重写需要注意的细节问题:
子类方法覆盖父类方法,必须要保证权限大于等于父类权限。
class Fu(){
void show(){}
public void method(){}
}
class Zi() extends Fu{
public void show(){} //编译运行没问题
void method(){} //编译错误
}
写法上稍微注意:必须一模一样:方法的返回值类型 方法名 参数列表都要一样。
总结:当一个类是另一个类中的一种时,可以通过继承,来继承属性与功能。如果父类具备的功能内容需要子类特殊定义时,进行方法重写。
1.7重写和重载的区别
/*
*1.重载(overload)和重写(override)的区别
- 修饰符 返回值类型 方法名(形参列表){
- }
- public int getSum(int a,int b){
- }
- public double getSum(double a,double b){
- }
- 重载不一定有继承关系,重写一定有继承关系
- a.修饰符:
-
重载对两个方法没有任何要求
-
重写对权限修饰符有要求,子类的方法的权限>=父类的方法的权限
- b.返回值类型:
-
重写要求子类的方法和父类中被重写的方法一致
-
重载对两个方法返回值类型没有要求
- c.方法名:
-
重写要求子类的方法和父类中被重写的方法方法名一致
-
重载要求两个方法方法名必须一致
- d.形参列表:
-
重写要求子类的方法和父类中被重写的方法的形参列表一致
-
重载要求两个方法的形参列表必须不同(形参类型不同,形参个数不同,形参类型顺序不同)
*/
//重载在继承中应用
//从父类中继承的方法和子类中的方法构成重载关系
class Father{
public int getSum(int a,int b){
return a+b;
}
}
class Son extends Father{
public double getSum(double a,double b){
return a+b;
}
public double getSum(double a,double b,double c){
return a+b+c;
}
}
第2章super关键字与super语句
2.1子父类中构造方法的调用
在创建子类对象时,父类的构造方法会先执行,因为子类中所有构造方法的第一行有默认的隐式super();语句。
格式:
调用本类中的构造方法
this(实参列表);
调用父类中的空参数构造方法
super();
调用父类中的有参数构造方法
super(实参列表);
为什么子类对象创建都要访问父类中的构造方法?因为子类继承了父类的内容,所以创建对象时,必须要先看父类是如何对其内容进行初始化的,看如下程序:
public class Fu {
public Fu(int i){
System.out.println(i);
}
public Fu(){
System.out.println(“Fu的构造方法”);
}
}
/*在每个类的构造方法的第一行默认有一个语句:
- super();
- 作用:
- 调用父类的构造方法
*注意:
- 可以通过super(参数)来调用父类中指定的构造方法
- 如果在第一行写了super(参数)语句,就没有默认的
- super();语句
*/
public class Zi extends Fu{
public Zi(){
//super();//即使不写默认也有
super(3);
System.out.println(“Zi的构造方法”);
}
}
public class Demo02 {
public static void main(String[] args) {
Zi zi=new Zi();
}
}
通过结果发现,子类构造方法执行时中,调用了父类构造方法,这说明,子类构造方法中有一句super()。
那么,子类中的构造方法为什么会有一句隐式的super()呢?
原因:子类会继承父类中的内容,所以子类在初始化时,必须先到父类中去执行父类的初始化动作。这样,才可以使用父类中的内容。
当父类中没有空参数构造方法时,子类的构造方法必须有显示的super语句,指定要访问的父类有参数构造方法。
2.2super语句案例
练习:描述学生和工人这两个类,将他们的共性name和age抽取出来存放在父类中,并提供相应的get和set方法,同时需要在创建学生和工人对象就必须明确姓名和年龄
public class Person {
private String name;
private int age;
public Person(){
}
/*Person的构造方法*/
public Person(String name,int age){//name="张工",age=23
this.name=name;//this.name="张工"
this.age=age;//this.age=23
}
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;
}
}
public class Worker extends Person{
/* public Worker(){
super();
}/
/
//由于工人也具有姓名,年龄,我们可以在工人中再定义姓名年龄
//但是父类Person中已经定义了name,age,我们在子类中再定义
//name,age,显得重复,代码复用性很低
private String name;
private int age;
public Worker(String name,int age){
this.name=name;
this.age=age;
}*/
public Worker(String name,int age){//name="张工" age=23
super(name,age);//super("张工",23);
}
public void work(){
System.out.println(getName()+"工人在努力工作");
}
}
2.3super()语句注意事项
1.如果子类的构造方法第一行写了this语句调用了本类其他构造方法,那么super调用父类的语句还有吗?
这时是没有的,因为this()或者super(),只能定义在构造方法的第一行,因为初始化动作要先执行。
2.父类构造方法中是否有隐式的super语句呢?
也是有的。记住:只要是构造方法默认第一行都是super();
父类的父类是谁呢?super调用的到底是谁的构造方法呢?
Java体系在设计,定义了一个所有对象的父类Object
注意:
类中的构造方法默认第一行都有隐式的super()语句,在访问父类中的空参数构造方法。所以父类的构造方法既可以给自己的对象初始化,也可以给自己的子类对象初始化。
如果默认的隐式super()语句在父类中没有对应的构造方法,那么必须在构造方法中通过this或者super的形式明确要调用的构造方法。
第3章抽象类
3.1抽象类-产生
当编写一个类时,我们往往会为该类定义一些方法,这些方法是用来描述该类的功能具体实现方式,那么这些方法都有具体的方法体。
但是有的时候,某个父类只是知道子类应该包含怎么样的方法,但是无法准确知道子类如何实现这些方法。比如一个图形类应该有一个求周长的方法,但是不同的图形求周长的算法不一样。那该怎么办呢?
分析事物时,发现了共性内容,就出现向上抽取。会有这样一种特殊情况,就是方法功能声明相同,但方法功能主体不同。那么这时也可以抽取,但只抽取方法声明,不抽取方法主体。那么此方法就是一个抽象方法。
当定义了抽象函数的类也必须被abstract关键字修饰,被abstract关键字修饰的类是抽象类。
3.2抽象类&抽象方法的定义
抽象方法定义的格式:
public abstract 返回值类型 方法名(参数);
抽象类定义的格式:
abstract class 类名 {
}
看如下代码:
/* 当我们画一个形状的时候,我们可以求出这个形状的周长和面积
- 长方形
- 周长:2*(长+宽)
- 面积:长*宽
- 正方形
- 周长:4*边长
- 面积:边长*边长
- 抽象方法的由来:
-
由于在我们形状类中,这个形状是不确定的,因此求周长的方法体和求面积的方法体都无法写
-
因此我们就不写这两个方法的方法体,但是需要在返回值类型前面加上abstract
- 抽象类的由来:
-
含有抽象方法的类一定是抽象类(在class的前面加abstract修饰)
*/
//定义了一个形状类
public abstract class Shape {
//求周长的方法
/*
*形状类中定义的求周长的方法
* 在周长的方法中,你既不能定义长方形的求周长的公式
* 也不能定义正方形的求周长的公式
/
public abstract int getZhouChang();
/
*形状类中定义的求面积的方法
* 在面积的方法中,你既不能定义长方形的求面积的公式
* 也不能定义正方形的求面积的公式
*/
public abstract int getArea();
}
//长方形是形状一种
//在长方形类中需要重写所有的抽象方法,才能保证长方形类不是抽象类
public class ChangFangXing extends Shape{
int length;//长方形的长
int width;//长方形的宽
@Override
public int getZhouChang() {
return 2*(width+length);
}
@Override
public int getArea() {
return width*length;
}
}
public class ZhengFangXing extends Shape{
int b;//边长
@Override
public int getZhouChang() {
// TODO Auto-generated method stub
return 4*b;
}
@Override
public int getArea() {
// TODO Auto-generated method stub
return b*b;
}
}
public class Demo {
public static void main(String[] args) {
// Shape s=new Shape();//由于Shape是一个抽象类,不能创建对象
ChangFangXing cfx=new ChangFangXing();
cfx.length=3;
cfx.width=7;
System.out.println(cfx.getZhouChang());
System.out.println(cfx.getArea());
ZhengFangXing zfx=new ZhengFangXing();
zfx.b=5;
System.out.println(zfx.getZhouChang());
System.out.println(zfx.getArea());
}
}
3.3抽象类的特点:
1、抽象类和抽象方法都需要被abstract修饰。抽象方法一定要定义在抽象类中。
2、抽象类不可以直接创建对象,原因:调用抽象方法没有意义。
3、只有重写了抽象类中所有的抽象方法后,其子类才可以创建对象。否则该子类还是一个抽象类。
之所以继承抽象类,更多的是在思想,是面对共性类型操作会更简单。
3.4抽象类的细节问题:
1、抽象类一定是个父类?
是的,因为不断抽取而来的。
2、抽象类中是否可以不定义抽象方法。
是可以的,那这个抽象类的存在到底有什么意义呢?不让该类创建对象,方法可以直接让子类去使用
3、抽象关键字abstract不可以和哪些关键字共存?
1、private:私有的方法子类是无法继承到的,也不存在覆盖,而abstract和private一起使用修饰方法,abstract既要子类去实现这个方法,而private修饰子类根本无法得到父类这个方法。互相矛盾。
2、final,暂时不关注,后面学
3、static,暂时不关注,后面学
第4章综合案例—员工类系列定义
4.1案例介绍
某IT公司有多名员工,按照员工负责的工作不同,进行了部门的划分(研发部员工、维护部员工)。研发部根据所需研发的内容不同,又分为JavaEE工程师、Android工程师;维护部根据所需维护的内容不同,又分为网络维护工程师、硬件维护工程师。
公司的每名员工都有他们自己的员工编号、姓名,并要做它们所负责的工作。
工作内容
JavaEE工程师:员工号为xxx的 xxx员工,正在研发淘宝网站
Android工程师:员工号为xxx的 xxx员工,正在研发淘宝手机客户端软件
网络维护工程师:员工号为xxx的 xxx员工,正在检查网络是否畅通
硬件维护工程师:员工号为xxx的 xxx员工,正在修复打印机
请根据描述,完成员工体系中所有类的定义,并指定类之间的继承关系。进行XX工程师类的对象创建,完成工作方法的调用。
员工类:
属性:工号
方法:抽象方法 work()
研发部员工:
属性:类型
方法:抽象方法 work()
必须提供带参构造方法:
4.2案例分析
根据上述部门的描述,得出如下的员工体系图
根据员工信息的描述,确定每个员工都有员工编号、姓名、要进行工作。则,把这些共同的属性与功能抽取到父类中(员工类),关于工作的内容由具体的工程师来进行指定。
分析:
父类—员工类
属性:工号
方法:抽象的方法 工作
工作内容
JavaEE工程师:员工号为xxx的 xxx员工,正在研发淘宝网站
Android工程师:员工号为xxx的 xxx员工,正在研发淘宝手机客户端软件
网络维护工程师:员工号为xxx的 xxx员工,正在检查网络是否畅通
硬件维护工程师:员工号为xxx的 xxx员工,正在修复打印机
创建JavaEE工程师对象,完成工作方法的调用
4.3案例代码实现
根据员工体系图,完成类的定义
定义员工类(抽象类)
public abstract class Employee {
private String id;// 员工编号
private String name; // 员工姓名
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
//工作方法(抽象方法)
public abstract void work();
}
定义研发部员工类Developer 继承 员工类Employee
public abstract class Developer extends Employee {
}
定义维护部员工类Maintainer 继承 员工类Employee
public abstract class Maintainer extends Employee {
}
定义JavaEE工程师 继承 研发部员工类,重写工作方法
public class JavaEE extends Developer {
@Override
public void work() {
System.out.println(“员工号为 " + getId() + " 的 " + getName() + " 员工,正在研发淘宝网站”);
}
}
定义Android工程师 继承 研发部员工类,重写工作方法
public class Android extends Developer {
@Override
public void work() {
System.out.println(“员工号为 " + getId() + " 的 " + getName() + " 员工,正在研发淘宝手机客户端软件”);
}
}
定义Network网络维护工程师 继承 维护部员工类,重写工作方法
public class Network extends Maintainer {
@Override
public void work() {
System.out.println(“员工号为 " + getId() + " 的 " + getName() + " 员工,正在检查网络是否畅通”);
}
}
定义Hardware硬件维护工程师 继承 维护部员工类,重写工作方法
public class Hardware extends Maintainer {
@Override
public void work() {
System.out.println(“员工号为 " + getId() + " 的 " + getName() + " 员工,正在修复打印机”);
}
}
在测试类中,创建JavaEE工程师对象,完成工作方法的调用
public class Test {
public static void main(String[] args) {
//创建JavaEE工程师员工对象
JavaEE ee = new JavaEE();
//设置该员工的编号
ee.setId(“000015”);
//设置该员工的姓名
ee.setName(“小明”);
//调用该员工的工作方法
ee.work();
}
}
第5章Java的API及Object类
在以前的学习过程中,我们都在学习对象基本特征、对象的使用以及对象的关系。接下来我们开始使用对象做事情,那么在使用对象做事情之前,我们要学习一些API中提供的常用对象。首先在学习API中的Object类之前,先来学习如何使用API。
5.1Java 的API
Java 的API(API: Application(应用) Programming(程序) Interface(接口))
Java API就是JDK中提供给我们使用的类,这些类将底层的代码实现封装了起来,我们不需要关心这些类是如何实现的,只需要学习这些类如何使用即可:
例如Scanner
Scanner scan=new Scanner(System.in);
Int i=scan.nextInt();
在JDK安装目录下有个src.zip文件,这个文件解压缩后里面的内容是所有Java类的源文件。可以在其中查看相对应的类的源码。
我们在每次查看类中的方法时,都打开源代码进行查看,这种方式过于麻烦。其实,我们可以通过查帮助文档的方式,来了解Java提供的API如何使用。如下图操作:查找Object类
通过帮助文档中类与方法的介绍,我们就能够使用这个类了。
5.2Object类概述
Object类是Java语言中的根类,即所有类的父类。它中描述的所有方法子类都可以使用。所有类在创建对象的时候,最终找的父类就是Object。
在Object类众多方法中,我们先学习equals方法与toString方法,其他方法后面课程中会陆续学到。
5.3equals方法
5.3.1Object原始equals()方法分析:
equals方法,用于比较两个对象是否相同,它其实就是使用两个对象的内存地址在比较。Object类中的equals方法内部使用的就是==比较运算符。
package com.whhp.object01;
public class Person/extends Object/ {
}
package com.whhp.object01;
/*
*Object类:
-
所有类都直接或间接继承Object类,所有类都继承了Object类中非私有的方法
-
boolean equals(Object obj):比较两个对象,结果要么true,要么是false
-
String toString()
-
ctrl+shift+T 输入类名快速打开一个类
-
ctrl+o 在这个类中找成员,输入成员变量或成员方法的名字可以快速定位
-
Object类中的equals方法比较的是两个对象的地址值
*/
public class Demo01 {
public static void main(String[] args) {
//使用Object类中的equals方法
Person p1=new Person();//p1=1344 (new Person()地址值)
Person p2=new Person();//p2=2355 (new Person()地址值)System.out.println(p1);//打印p1的地址值com.whhp.object01.Person@2e807f85
System.out.println(p2);//打印p2的地址值com.whhp.object01.Person@76340c9cboolean b=p1.equals(p2);//用的是从Object类中继承下来的equals方法
System.out.println(b);boolean b2=p1.equals(p1);
System.out.println(b2);
}
}
/*
*Object类中equals源码分析: -
public boolean equals(Object obj) {
return (this == obj);
//this=p1=1344
//obj=p2=2355
//相当于 p1==p2}
-
==:
-
如果比较引用类型比较的是两个引用类型变量存放地址值
-
如果比较基本类型,比较的是数值
-
int i=3;
-
int j=6
-
System.out.println(i==j);//false
*/
5.3.2重写equals()方法
在开发中要比较两个对象是否相同,经常会根据对象中的属性值进行比较,也就是在开发经常需要子类重写equals方法根据对象的属性值进行比较。如下代码演示:
注意:在复写Object中的equals方法时,一定要注意public boolean equals(Object obj)的参数是Object类型,在调用对象的属性时,一定要进行类型转换,在转换之前必须进行类型判断。
5.3.2.1一个属性的重写
package com.whhp.object02;
/*
*
- 我们创建两个Person对象代表两个人,我们利用equals(Object obj)方法
- 比较两个Person对象,其实比较的是两个人对象的地址值
- 但是从现实生活去考虑,我们判断两个人是否是同一个人,我们通常通过姓名,年龄
- 身高等 来判断两个人是否是同一个人
- 假设按照年龄属性来判断:
- 如果两个人的年龄相同,我们就可以认为两个人是同一个人
- 如果两个人的年龄不同,我们认为两个人不是同一个人
*/
public class Person/* extends Object */{
private int age;
public Person(int age) {
this.age = age;
}
// 由于Object类中的equals方法不能贴近我们的现实需求
// 因此我们重写
@Override
public boolean equals(Object obj) {//Object obj=new Obejct();
/*如果两个人的年龄相同,我们就可以认为两个人是同一个人,返回true
* 如果两个人的年龄不同,我们认为两个人不是同一个人,返回false
*/
if(this==obj){//代表调用equals方法的对象和传入的对象是同一个对象
//就没有必要在比较两者年龄,因为就是同一个人,属性肯定相同
return true;
}
if(!(obj instanceof Person)){//obj指向的类型能否转换为Person类型
return false;
}
//为了使用Person中特有属性 age,因此我们要将obj向下转型
Person p=(Person)obj; //Person p=(Person)new Object();//无法转换
/*if(this.age==p.age){
return true;
}else{
return false;
}*/
return this.age==p.age;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
package com.whhp.object02;
public class Demo02 {
public static void main(String[] args) {
//method01();
//method02();
Person p1=new Person(10);
boolean b=p1.equals(p1);
System.out.println(b);
}
private static void method02() {
Person p1=new Person(10);
Object obj=new Object();
boolean b= p1.equals(obj);
System.out.println(b);
}
private static void method01() {
Person p1=new Person(10);
Person p2=new Person(20);
boolean b=p1.equals(p2);
System.out.println(b);
}
}
5.3.2.2多个属性的重写
package com.whhp.object03;
/* Person类中有两个属性
- 姓名,年龄
- 比较两个人的姓名和年龄来判断是否是同一个人
- 如果两个人的姓名和年龄均相同,认为是同一个人
- 如果两个人的姓名和年龄不同,我们认为两个人不是同一个人
- alt+shift+s 选择生成构造方法使用字段
- alt+shift+s 选择生成getter/setter方法
- 结论:
- 重写equals的目标:
-
如果所有的属性值均相同我们返回true
-
至少有一个属性值不同返回false
*/
public class Person {
private String name;
private int age;
public Person(String name, int age) {
super();
this.name = name;
this.age = age;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (this.getClass() != obj.getClass())//if (!(obj instanceof Person))
return false;
Person other = (Person) 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) {
// 如果两个人的姓名和年龄均相同,认为是同一个人
// 如果两个人的姓名和年龄不同,我们认为两个人不是同一个人
if (this == obj) {// 代表调用equals方法的对象和传入的对象是同一个对象
// 就没有必要在比较两者年龄,因为就是同一个人,属性肯定相同
return true;
}
if (!(obj instanceof Person)) {// obj指向的类型能否转换为Person类型
return false;
}
// 为了使用Person类中特有属性,我们要把obj向下转型
Person p = (Person) obj;
// 通过this和p调用姓名以及年龄去比较
boolean b1 = this.age == p.age;
boolean b2 = this.name.equals(p.name);// 利用字符串的equals方法比较两个字符串内容
if (b1 && b2) {// 如果b1和b2都为真,说明两个人的姓名和年龄均相同
return true;
} else {// !b1 || !b2
// 两个人姓名和年龄至少有一个不同
return false;
}
}
/
/ 生成getter/setter方法 */
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;
}
}
package com.whhp.object03;
public class Demo03 {
public static void main(String[] args) {
Person p1=new Person(“张三”,10);
Person p2=new Person(“李四”,10);
boolean b=p1.equals(p2);
System.out.println(b);
}
}
5.4toString方法
toString方法返回该对象的字符串表示,其实该字符串内容就是对象的类型+@+内存地址值。
由于toString方法返回的结果是内存地址,而在开发中,经常需要按照对象的属性得到相应的字符串表现形式,因此也需要重写它。
class Person extends Object{
int age ;
//根据Person类的属性重写toString方法
public String toString() {
return “Person [age=” + age + “]”;
}
}
5.4.1Object原始toString()方法分析
public class Person{
}
/*
*当打印对象的时候默认会调用toString();
*
*
*/
public class Demo04 {
public static void main(String[] args) {
method01();
}
private static void method01() {
Person p = new Person();
System.out.println(p.toString());// com.whhp.object04.Person@177c760b
System.out.println(p);// com.whhp.object04.Person@177c760b
}
}
/*
- toString()源码分析: public String toString() { return this.getClass().getName() +
- “@” + Integer.toHexString(hashCode()); }
- this.getClass().getName()//获取this引用指向的对象所属的 类的名字(包名.类名)
- //com.whhp.object04.Person
- Integer.toHexString(hashCode())//获取对象的内存地址值的十六进制形式
*/
5.4.2重写toString()方法
package com.whhp.object04;
/*
- 我们通过对象调用toString()方法打印对象的地址值没有意义
- toString重写目标:
- 返回该类中所有成员变量字符串的拼接形式
*/
public class Person {
private String name;
private int age;
public Person(){
}
public Person(String name, int age) {
super();
this.name = name;
this.age = age;
}
//重写掉Object中原始的toString()方法
/*@Override
public String toString() {
return "name="+name+",age="+age;
}*/
@Override
public String toString() {
return "Person [name=" + name + ", 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;
}
}
package com.whhp.object04;
/*
*当打印对象的时候默认会调用toString();
*
*
*/
public class Demo04 {
public static void main(String[] args) {
//method01();
Person p=new Person(“张三”,10);
System.out.println(p.toString());
System.out.println§;
}
private static void method01() {
Person p = new Person();
System.out.println(p.toString());// com.whhp.object04.Person@177c760b
System.out.println(p);// com.whhp.object04.Person@177c760b
}
}
/*
-
toString()源码分析: public String toString() { return this.getClass().getName() +
-
“@” + Integer.toHexString(hashCode()); }
-
this.getClass().getName()//获取this引用指向的对象所属的 类的名字(包名.类名)
-
//com.whhp.object04.Person
-
Integer.toHexString(hashCode())//获取对象的内存地址值的十六进制形式
*/
第6章 final关键字
6.1final的概念
继承的出现提高了代码的复用性,并方便开发。但随之也有问题,有些类在描述完之后,不想被继承,或者有些类中的部分方法功能是固定的,不想让子类重写。可是当子类继承了这些特殊类之后,就可以对其中的方法进行重写,那怎么解决呢?
要解决上述的这些问题,需要使用到一个关键字final,final的意思为最终,不可变。final是个修饰符,它可以用来修饰类,类的成员,以及局部变量。
6.2final的特点
final修饰类不可以被继承,但是可以继承其他类。public final class Fu {
}
/*public class Zi extends Fu {
}
*/
final修饰的方法不可以被重写
public class Fu{
public finl void method(){
}
}
public class Zi extends Fu{
/*
public void method(){
}
*/
}
final修饰的变量称为常量,这些变量只能赋值一次。
public final class Fu {
final int number=4;
final int age;
public Fu(){
//number=5;//加final修饰的成员变量只能赋值一次
age=13;
//age=8;
}
public static void main(String[] args) {
//1.final修饰局部变量第一种格式
final int i=3;
//i=10;
System.out.println(i);
//2.final修饰局部变量的第二种格式
final int j;
j=6;
//j=5;
}
}
引用类型的变量值为对象地址值,地址值不能更改,但是地址内的对象属性值可以修改。
//3.final修饰引用变量
final Fu fu=new Fu();//Fu fu=1311
//fu的值恒定不变
//fu只能赋值一次
//fu=new Fu();//fu=1255,此时无法给fu赋值
final Fu fu2;
fu2=new Fu();
// fu2=new Fu();
修饰成员变量,需要在创建对象前赋值,否则报错。(当没有显式赋值时,多个构造方法的均需要为其赋值。)
public final class Fu {
final int number=4;
final int age;
public Fu(){
//number=5;//加final修饰的成员变量只能赋值一次
age=13;
//age=8;
}
}
需要免费的java基础视频和java企业级开发视频以及项目实战(包含SSM框架详细讲解、activiti流程引擎、springboot、springcloud视频讲解和项目实战课程)可以添加qq:1743337375