Java开发零基础篇:day08 面向对象(二)

简介: 面向对象

 this 关键字(一)

this关键字

之前说过,变量名称或方法参数名称,要见名知意,下列两个set方法的参数名,就显得太LOW了。

public class Student { 
    private String name; 
    private int age;
    public void setName(String n) { 
        name = name;
    }
    public void setAge(int a) { 
        age = age;
    }
    public String getName() { 
        return name;
    }
    public int getAge() { 
        return age;
    }
}

image.gif

不就是设置名字和年龄吗,如果此时把参数名分别改为name和age。

public class Student { 
    private String name; 
    private int age;
    public void setName(String name) { 
        name = name;
    }
    public void setAge(int age) { 
        age = age;
    }
    public String getName() { 
        return name;
    }
    public int getAge() { 
        return age;
    }
}

image.gif

此时会发现参数根本就设置不进去,name和age打印出来都是各自的初始值,运行测试类的结果如下:

null,0

image.gif

导致参数设置不进去的原因是:

局部变量和成员变量同名,此时在方法中调用变量时根据就近原则,优先使用局部变量,示意图如

下。

image.gif编辑

可以看出setName方法中两次使用的name,都是直接寻找距离自己最近的形参name,就相当于把参数name的值设置给参数name,根本就没有把参数值设置给成员变量。

注意:

当在一个作用域访问变量时,首先在当前作用域查找该变量,

如果能找到,不继续查找。

如果在当前作用域找不到该变量,尝试去上一层作用域查找,如果找到,停止查找,如果找不 到,继续上一层查找,依次类推。这整个过程形成一个查找链,这个称为作用域链。

该问题,更专业的叫法是局部变量和成员变量存在二义性,也就是变量名有歧义。

为了解决该问题——有请this关键字。

使用 this.变量名 的语法,此时访问的就是成员变量,this的其他操作,后面再讲解。

image.gif编辑

具体代码如下:

public class Student { 
    private String name; 
    private int age;
    public void setName(String name) { 
        this.name = name;
    }
    public void setAge(int age) { 
        this.age = age;
    }
    public String getName() { 
        return name;
    }
    public int getAge() { 
        return age;
    }
}

image.gif

使用构造器还是setter方法

构造器和setter方法都可以给对象设置数据:

构造器,在创建对象的时候设置初始数据,只能初始化一次。

setter方法,创建对象后再设置初始数据,可以设置多次。

继承思想

(1)使用面向对象的知识定义出老师(Teacher)、学生(Student)、员工(Employee)三个类:

老师:拥有名字、年龄、级别三个状态,有授课和休息两个功能

学生:拥有名字、年龄、学号三个状态,有学习和休息两个功能

员工:拥有名字、年龄、入职时间三个状态,有工作和休息两个功能

代码截图如下:

image.gif编辑

此时,发现三个类中的存在着大量的共同代码,而我们要考虑的就是如何解决代码重复的问题。 面向对象的继承思想,可以解决多个类存在共同代码的问题。

继承关系设计图:

image.gif编辑

被继承的类,称之为父类、基类;

继承父类的类,称之为子类,拓展类;

父类:存放多个子类共同的字段和方法;

子类:存放自己特有的字段和方法。

继承语法

在java程序中,如果一个类需要继承另一个类,此时使用extends关键字。

public class 子类名 extends 父类名{
}

image.gif

注意:

Java中类只支持单继承,但是支持多重继承。也就是说一个子类只能有一个直接的父类,父类也可以再有父类。

下面是错误的写法! Java中的类只支持单继承。

class SuperClass1{} 
class SuperClass2{}
class SubClass extends SuperClass1,SuperClass2{} //错误

image.gif

下面代码是正确的。一个父类可以有多个子类。

class SuperClass{}
class SubClass1 extends SuperClass{} 
class SubClass2 extends SuperClass{}

image.gif

下面代码是正确的,支持多重继承。

class SuperSuperClass{}
class SuperClass extends SuperSuperClass{} 
class SubClass extends SuperClass

image.gif

Object类是Java语言的根类,任何类都是Object的子类,要么是直接子类,要么是间接子类;

public class    Person{} 等价于 public class Person extends Object{}

image.gif

继承操作

父类代码:

public class Person { 
    private String name; 
    private int age;
    public void rest() { 
        System.out.println("休息");
    }
    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;
    }
}

image.gif

子类代码:

public class Student extends Person{ 
    private String sn;// 学号
    public void study() { 
        System.out.println("学习");
    }
    public String getSn() { 
        return sn;
    }
    public void setSn(String sn) { 
        this.sn = sn;
    }
}

image.gif

测试类:

public class ExtendsDemo {
    public static void main(String[] args) {
        //创建学生对象
        Student stu = new Student();
        //设置字段信息
        stu.setName("will");    //继承了父类
        stu.setAge(17); //继承了父类
        stu.setSn("s_123"); 
        //调用方法
        stu.study();
        stu.rest();//继承了父类
    }
}

image.gif

子类可以继承到父类哪些成员

子类继承父类之后,可以拥有到父类的某一些成员(字段和方法),根据访问修饰符来判断:

如果父类中的成员使用public和protected修饰,子类都能继承;

如果父类和子类在同一个包中,使用缺省访问修饰的成员,此时子类可以继承到 ;

如果父类中的成员使用private修饰,子类继承不到。private只能在本类中访问;

父类的构造器,子类也不能继承,因为构造器必须和当前的类名相同。

关键字 本类 同包子类 同包其他类 不同包子类 不同包其他类
private
默认
protected
public

private :私有的,本类可见。

默认 ( friendly ) : 默认的,同包可见。

protected : 受保护的。①同包可见 ②子类可见

public : 公共的,任意地方都可见。

访问权限 : private < 默认 < protected < public

方法覆盖

子类继承了父类,可以拥有父类的部分方法和成员变量。可是当父类的某个方法不适合子类本身的特征时,此时怎么办?比如鸵鸟(Ostrich)是鸟类(Bird)中的一个特殊品种,所以鸵鸟类是鸟类的一个子类,但是鸟类有飞翔的功能,但是对应鸵鸟,飞翔的行为显然不适合于它。

父类:

public class Bird { 
    public void fly() {
        System.out.println("飞呀飞...");
    }
}

image.gif

子类:

public class Ostrich extends Bird{
}

image.gif

测试类:

public class OverrideDemo {
    public static void main(String[] args) {
        //创建鸵鸟对象
        Ostrich os = new Ostrich();
        //调用飞翔功能
        os.fly();
    }
}

image.gif

控制台输出:

飞呀飞...

image.gif

上述代码从语法是正确的,但从逻辑上是不合理的,因为鸵鸟不能飞翔,此时怎么办?——方法覆盖操作。

方法覆盖操作

当子类存在一个和父类一模一样的方法时,我们就称之为子类覆盖了父类的方法,也称之为重写。 那么我们就可以在子类方法体中,重写编写逻辑代码。

public class Ostrich extends Bird{ 
    public void fly() {
        System.out.println("扑扑翅膀,快速奔跑...");
    }
}

image.gif

控制台输出:

扑扑翅膀,快速奔跑...

image.gif

方法的调用顺序

通过对象调用方法时,先在子类中查找有没有对应的方法,若存在就执行子类的,若子类不存在就 执行父类的,如果父类也没有,报错。

方法覆盖的细节

private修饰的方法不能被子类所继承,也就不存在覆盖的概念。

(1)实例方法签名必须相同 (方法签名= 方法名 + 方法的参数列表);

(2)子类方法的返回值类型是和父类方法的返回类型相同或者是其子类;

(3)子类方法的访问权限比父类方法访问权限更大或相等;

如果父类方法是private,子类方法不能重写。==> 重写建立在继承的基础上,没有继承,就不能重写。

(4)子类方法中声明抛出的异常小于或等于父类方法声明抛出异常类型。

super 关键字(一)

问题,在子类中的某一个方法中需要去调用父类中被覆盖的方法,此时得使用super关键字。

public class Ostrich extends Bird{ 
    public void fly() {
        System.out.println("扑扑翅膀,快速奔跑...");
    }
    public void say() {
        super.fly();//调用父类被覆盖的方法fly();//调用本类中的方法
    }
}

image.gif

如果调用被覆盖的方法不使用super关键字,此时调用的是本类中的方法。

super关键字表示父类对象的意思,更多的操作,后面再讲解。

super.fly()可以翻译成调用父类对象的fly方法。

抽象方法和抽象类

(1)求圆(Circle)和矩形(Rectangle)两种图形的面积。

分析:无论是圆形还是矩形,还是其他形状的图形,只要是图形,都有面积,也就说图形都有求面 积的功能,那么我们就可以把定义一个图形(Graph)的父类,该类拥有求面积的方法,但是作为图形 的概念,而并不是某种具体的图形,那么怎么求面积是不清楚的,姑且先让求面积的getArea方法返回 0。

父类代码:

public class Graph {
    public double getArea() { 
        return 0.0;
    }
}

image.gif

子类代码(圆形):

public class Circle extends Graph { 
    private int r;  //半径
    public void setR(int r) { 
        this.r = r;
    }
    public double getArea() { 
        return 3.14 * r * r;
    }
}

image.gif

子类代码(矩形):

public class Rectangle extends Graph { 
    private int width; // 宽度
    private int height; // 高度
    public void setWidth(int width) { 
        this.width = width;
    }
    public void setHeight(int height) { 
        this.height = height;
    }
    public double getArea() { 
        return width * height;
    }
}

image.gif

测试类:

public class GraphDemo {
    public static void main(String[] args) {
        // 圆
        Circle c = new Circle(); c.setR(10);
        double ret1 = c.getArea(); 
        System.out.println("圆的面积:" + ret1);
        // 矩形
        Rectangle r = new Rectangle(); 
        r.setWidth(5); 
        r.setHeight(4);
        double ret2= r.getArea(); 
        System.out.println("矩形的面积:" + ret2);
    }
}

image.gif

控制台输出:

圆的面积:314.0 
矩形的面积:20.0

image.gif

引出抽象方法

问题1:既然不同的图形求面积的算法是不同的,所以必须要求每一个图形子类去覆盖getArea方法,如果没有覆盖,应该以语法报错的形式做提示。

问题2:在Graph类中的getArea方法的方法体没有任何存在意义,因为不同图形求面积算法不一样,子类必须要覆盖getArea方法。

要满足上述对方法的要求,就得使用abstract来修饰方法,被abstract修饰的方法具备两个特征:

该方法没有方法体

要求子类必须覆盖该方法

这种方法,我们就称之为抽象方法。

抽象方法和抽象类

使用abstract修饰的方法,称为抽象方法。

public abstract 返回类型 方法名(参数);

image.gif

抽象方法的特点:

使用abstract修饰,没有方法体,留给子类去覆盖

抽象方法必须定义在抽象类或接口中

使用abstract修饰的类,成为抽象类。

public abstract class 类名{
}

image.gif

一般的,抽象类以Abstract作为类名前缀,如AbstractGraph,一看就能看出是抽象类。

抽象类的特点:

抽象类不能创建对象,调用没有方法体的抽象方法没有任何意义

抽象类中可以同时拥有抽象方法和普通方法

抽象类要有子类才有意义,子类必须覆盖父类的抽象方法,除非子类也是抽象类。

父类代码:

public abstract class AbstractGraph {
    public abstract double getArea();   //没有方法体
}

image.gif

子类代码:

public class Circle extends AbstractGraph { 
    private int r;// 半径
    public void setR(int r) { 
        this.r = r;
    }
    public double getArea() {   //覆盖父类抽象方法
        return 3.14 * r * r;    //编写方法体
    }
}

image.gif

测试类没有改变。

Object类和常用方法

Object本身表示对象类的意思,是Java中的根类,要么是一个类的直接父类,要么就是一个类的间接父类。

class A{}  其实等价于  class A extends Object{}

因为所有类都是Object类的子类, 所有类的对象都可以调用Object类中的方法,常见的方法:

boolean equals(Object obj):拿当前调用该方法的对象和参数obj做比较

在Object类中的equals方法和“ == ”符号相同都是比较对象是否是同一个的存储地址。

public class ObjectDemo {
    public static void main(String[] args) {
        //创建Person对象p1
        Person p1 = new Person();
        //创建Person对象p2
        Person p2 = new Person();
        //比较p1和p2的内存地址是否相同
        boolean ret1 = p1 == p2; 
        boolean ret2 = p1.equals(p2);
        System.out.println(ret1);   //false 
        System.out.println(ret2);   //false
    }
}

image.gif

官方建议:每个类都应该覆盖equals方法去比较我们关心的数据,而不是内存地址。

String toString():表示把对象中的字段信息转换为字符串格式

打印对象时其实打印的就是对象的方法

Person p = new Person(); 
p.setName("will"); 
p.setAge(17); 
System.out.println(p);
System.out.println(p.toString());

image.gif

其中:

System.out.println(p); 等价于 System.out.println(p.toString());

image.gif

打印格式如:

ink.banq.demo.Person@15db9742

image.gif

默认情况下打印的是对象的值,但是我们更关心对象中字段存储的数据。

官方建议:应该每个类都应该覆盖返回我们关心的数据,如:

public class Person { 
    private String name; 
    private int 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;
    }
    public String toString() {
        return "Person [name=" + name + ", age=" + age + "]";
    }
}

image.gif

此时打印对象,看到的是该对象的字段信息。

Person [name=will, age=17]

image.gif

== 符号到底比较的是什么

比较基本数据类型:比较两个值是否相等;

比较对象数据类型:比较两个对象是否是同一块内存空间;

每一次使用new关键字,都表示在堆中创建一块新的内存空间。

以上就是Java入门第八天的全部内容了。

资料文档地址:Java开发零基础篇:day08面向对象(二).pdf

相关文章:Java开发零基础篇:day08 面向对象(一)

                 Java开发零基础篇:day09 面向对象(三)

                Java开发零基础篇:day10 面向对象(四)

目录
相关文章
|
23天前
|
Java
【Java基础面试十三】、面向对象的三大特征是什么?
这篇文章介绍了面向对象程序设计的三大基本特征:封装、继承和多态,其中封装隐藏对象实现细节,继承实现软件复用,多态允许子类对象表现出不同的行为特征。
【Java基础面试十三】、面向对象的三大特征是什么?
|
7天前
|
数据采集 Java 数据挖掘
Java IO异常处理:在Web爬虫开发中的实践
Java IO异常处理:在Web爬虫开发中的实践
|
9天前
|
数据采集 存储 前端开发
Java爬虫开发:Jsoup库在图片URL提取中的实战应用
Java爬虫开发:Jsoup库在图片URL提取中的实战应用
|
13天前
|
存储 搜索推荐 Java
探索安卓开发中的自定义视图:打造个性化UI组件Java中的异常处理:从基础到高级
【8月更文挑战第29天】在安卓应用的海洋中,一个独特的用户界面(UI)能让应用脱颖而出。自定义视图是实现这一目标的强大工具。本文将通过一个简单的自定义计数器视图示例,展示如何从零开始创建一个具有独特风格和功能的安卓UI组件,并讨论在此过程中涉及的设计原则、性能优化和兼容性问题。准备好让你的应用与众不同了吗?让我们开始吧!
|
19天前
|
Java 开发者 C++
|
21天前
|
IDE Java 开发工具
快速上手指南:如何用Spring Boot开启你的Java开发之旅?
【8月更文挑战第22天】Spring Boot由Pivotal团队开发,简化了Spring应用的创建过程。本文详述了从零开始搭建Spring Boot项目的步骤:首先确保安装了新版JDK、Maven/Gradle及IDE如IntelliJ IDEA或Eclipse;接着访问Spring Initializr网站(start.spring.io),选择所需依赖(如Web模块)并生成项目;最后,使用IDE打开生成的项目,添加`@SpringBootApplication`注解及main方法来启动应用。通过这些步骤,即便是新手也能快速上手,专注于业务逻辑的实现。
30 1
|
21天前
|
存储 Java
Java面向对象面试题总结(上)
在Java中,重写(Override)与重载(Overload)是两个重要的概念,关联到方法的定义与调用。重写是指子类对继承自父类的方法进行新的实现,以便提供子类特有的行为,其关键在于方法签名一致但方法体不同。重载则允许在同一个类中定义多个同名方法,只要参数列表不同即可,以此提供方法调用的灵活性。重写关注多态性,而重载强调编译时多态。
18 1
|
11天前
|
C# Windows 开发者
当WPF遇见OpenGL:一场关于如何在Windows Presentation Foundation中融入高性能跨平台图形处理技术的精彩碰撞——详解集成步骤与实战代码示例
【8月更文挑战第31天】本文详细介绍了如何在Windows Presentation Foundation (WPF) 中集成OpenGL,以实现高性能的跨平台图形处理。通过具体示例代码,展示了使用SharpGL库在WPF应用中创建并渲染OpenGL图形的过程,包括开发环境搭建、OpenGL渲染窗口创建及控件集成等关键步骤,帮助开发者更好地理解和应用OpenGL技术。
49 0
|
11天前
|
开发者 Java Spring
【绝技揭秘】掌握Vaadin数据绑定:一键同步Java对象,告别手动数据烦恼,轻松玩转Web应用开发!
【8月更文挑战第31天】Vaadin不仅是一个功能丰富的Java Web应用框架,还提供了强大的数据绑定机制,使开发者能轻松连接UI组件与后端Java对象,简化Web应用开发流程。本文通过创建一个简单的用户信息表单示例,详细介绍了如何使用Vaadin的`Binder`类实现数据绑定,包括字段与模型属性的双向绑定及数据验证。通过这个示例,开发者可以更专注于业务逻辑而非繁琐的数据同步工作,提高开发效率和应用可维护性。
31 0
|
11天前
|
存储 Java 数据库连接
Java编程之旅:从基础到高级,探索面向对象的力量
【8月更文挑战第31天】本文是一篇深入浅出的Java编程指南,旨在通过生动的例子和实际代码演示,带领读者从Java的基础语法起步,逐步深入到面向对象的核心概念,最后探讨如何在实际项目中应用这些知识。无论你是编程新手还是有一定经验的开发者,这篇文章都将为你提供有价值的见解和实用的技巧。