Java基础-封装和继承


Posted in Java/Android onJuly 02, 2021
目录
  • 一. 封装
    • 1.1 封装的目的
    • 1.2 封装的好处
    • 1.3 封装的步骤
    • 1.4 封装的例子
    • 1.5 小结
  • 二. 继承
    • 2.1 继承的介绍
    • 2.2 生活中的继承
    • 2.3 继承的好处
    • 2.4 继承的格式
    • 2.5 继承的demo
    • 2.6 子类不能继承的内容
      • 2.6.1 super 与 this 关键字
      • 2.6.2 构造器不能被继承
      • 2.6.3 final修饰的类不能被继承
    • 2.7 方法重写
      • 2.7.1 介绍
      • 2.7.2 使用场景与案例
      • 2.7.2 @Override重写注解
      • 2.7.3 注意事项
  • 总结

 

一. 封装

那封装是什么呢?

在面向对象程式设计方法中,封装(英语:Encapsulation)是指一种将抽象性函式接口的实现细节部分包装、隐藏起来的方法。

 

1.1 封装的目的

  • 直接通过操控类对象来达到目的,不需要对具体实现十分了解,使类属性和方法的具体实现对外不可见。不但方便还起到了保护作用。
  • 封装最主要的功能在于我们能修改自己的实现代码,而不用修改那些调用我们代码的程序片段。
  • 适当的封装可以让程式码更容易理解与维护,也加强了程式码的安全性。

 

1.2 封装的好处

  • 良好的封装能够减少耦合。
  • 类内部的结构可以自由修改。
  • 可以对成员变量进行更精确的控制。
  • 隐藏信息,实现细节。

 

1.3 封装的步骤

修改属性的可见性来限制对属性的访问(一般限制为private),例如:

public class Person {
    private String name ; // 姓名
    private String gender ; // 性别
    private int age; // 年龄
}

这段代码中,将 namesexage 属性设置为私有的,只能本类才能访问,其他类都访问不了,如此就对信息进行了隐藏。

对每个值属性提供对外的公共方法访问,也就是创建一对赋取值方法,用于对私有属性的访问,例如:

public class Person {
    private String name ; // 姓名
    private String gender ; // 性别
    private int age; // 年龄
    public void setName(String name) {
      	this.name = name;
    }
    public String getName() {
      	return name;
    }
	public void setGender(String gender) {
        this.gender = gender;
    }
    public String gender(){
        return gender;
    }
    public void setAge(int age) {
        this.age = age;
    }
    public int getAge() {
      	return age;
    }
}

采用 this 关键字调用本类中的属性,也就是类中的成员变量。主要为了解决实例变量(private String name)和局部变量(setName(String name)中的name变量)之间发生的同名的冲突。

 

1.4 封装的例子

创建一个用户类User:

Java基础-封装和继承

  • 代码如下:
package com.nz.pojo;
public class User {
    private String username; // 用户名
    private String password; // 密码
    public String getUsername() {
        return username;
    }
    public void setUsername(String username) {
        this.username = username;
    }
    public String getPassword() {
        return password;
    }
    public void setPassword(String password) {
        this.password = password;
    }
}
  • 编写测试User的demo:EncapsulationDemo

Java基础-封装和继承

代码如下:

package com.nz;
import com.nz.pojo.User;
public class EncapsulationDemo {
    public static void main(String[] args) {
        User user = new User();
        user.setUsername("太子爷哪吒");
        user.setPassword("520");
        System.out.println("username: " + user.getUsername() + "-----------"
                            + "password: " + user.getPassword());
    }
}

执行结果如下:

username:太子爷哪吒-----------password520

 

1.5 小结

封装实际上是将类的某些信息隐藏在类的内部,不允许外部程序直接访问,而是通过该类提供的方法来实现对隐藏信息的访问和操作。就是把我们想提供给外界的一些方法给暴露出来,以便外界能调用到我们。

 

二. 继承

 

2.1 继承的介绍

继承是java面向对象编程技术的一块基石,因为它允许创建分等级层次的类。描述的是事物之间的所属关系,这种关系是:is-a 的关系。

继承:就是子类继承父类的属性行为,使得子类对象(实例)可以直接具有与父类相同的属性、相同的行为。子类可以直接访问父类中的非私有的属性和行为。

 

2.2 生活中的继承

Java基础-封装和继承

兔子和长颈鹿属于食草动物类,老虎和狮子属于食肉动物类。而食草动物和食肉动物又是属于动物类。

那是不是兔子、长颈鹿、老虎、狮子都属于动物类呢?答案是没错滴!虽然食草动物和食肉动物都是属于动物,但是两者的属性和行为上有差别,所以子类会具有父类的一般特性也会具有自身的特性。我们就可以再多个类中存在相同属性和行为时,我们可以将这些内容抽取到单独一个类中,那么多个类无需再定义这些属性和行为,只要继承那一个类即可。

 

2.3 继承的好处

  1. 提高代码的复用性(减少代码冗余,相同代码重复利用)。
  2. 使类与类之间产生了关系。
  3. 子类拥有父类非 private 的属性、方法。
  4. 子类可以拥有自己的属性和方法,即子类可以对父类进行扩展。
  5. 子类可以用自己的方式实现父类的方法。
  6. 提高了类之间的耦合性(继承的缺点,耦合度高就会造成代码之间的联系越紧密,代码独立性越差)。
  7. Java 的继承是单继承,但是可以多重继承,单继承就是一个子类只能继承一个父类,多重继承就是,例如 B 类继承 A 类,C 类继承 B 类,所以按照关系就是 B 类是 C 类的父类,A 类是 B 类的父类,这是 Java 继承区别于 C++ 继承的一个特性。

 

2.4 继承的格式

在Java当中会通过extends关键字可以申明一个类是从另外一个类继承而来的,一般形式如下:

class 父类 {
}
class 子类 extends 父类 {
}

需要注意一点: Java 不支持多继承,但支持多重继承。就如下:

class A {
}
class B extends A {   (对的)
}
class C extends A, B {  (错的)
}
class C extends B {   (对的)
}

Java基础-封装和继承

顶层父类是Object类。所有的类默认继承Object,作为父类。

 

2.5 继承的demo

  • 编写一个父类极其对应的子类信息

结构如下:

Java基础-封装和继承

代码如下:

父类Person:

package com.nz.pojo;
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;
    }
}

子类Student没有额外的属性和方法:

package com.nz.pojo;
/**
 * 继承了Person特有的name, age,
 * 没有额外的独有属性和方法
 */
public class Student extends Person{
}

子类Teacher多了一个工资的属性和独有的教书方法:

package com.nz.pojo;
/**
 * 继承了Person特有的name, age,
 * 多了自己独有的工资属性还有独有的教书方法
 */
public class Teacher extends Person{
    // 工资
    private double salary ;
    // 特有方法
    public void teach(){
        System.out.println("老师在认真教书!");
    }
    public double getSalary() {
        return salary;
    }
    public void setSalary(double salary) {
        this.salary = salary;
    }
}

编写测试代码:

package com.nz;
import com.nz.pojo.Student;
import com.nz.pojo.Teacher;
public class InheritDemo {
    public static void main(String[] args) {
        Teacher teacher = new Teacher();
        teacher.setName("太子爷哪吒");
        teacher.setAge(18);
        teacher.setSalary(1999.99);
        System.out.println(teacher.getName());
        System.out.println(teacher.getAge());
        System.out.println(teacher.getSalary());
        teacher.teach();
        Student student = new Student();
        student.setName("哪吒");
        student.setAge(12);
        //student.setSalary(1999.99); // student没有工资属性,报错!
        System.out.println(student.getName());
        System.out.println(student.getAge());
    }
}

结果如下:

太子爷哪吒
18
1999.99
老师在认真教书!
哪吒
12

从结果来看,子类继承父类,就可以直接得到父类的成员变量和方法。而子类可以编写一些特有的属性和方法,但是是否可以继承所有成分呢?

 

2.6 子类不能继承的内容

并不是父类的所有内容都可以给子类继承的:

 

2.6.1 super 与 this 关键字

这里先将这两个关键字,super和this在继承关系中,运用比较频繁。

  • super关键字:我们可以通过super关键字来实现对父类成员的访问,用来引用当前对象的父类。
  • this关键字:指向自己本类的引用。

super和this完整的用法如下:

this.成员变量    	--    本类的
super.成员变量    	--    父类的
this.成员方法名()  	--    本类的    
super.成员方法名()   --    父类的

具体演示,创建测试InheritDemo2:

package com.nz;
public class InheritDemo2 {
    public static void main(String[] args) {
        Animal a = new Animal();
        a.eat();
        Cat cat = new Cat();
        cat.eatFish();
    }
}
class Animal {
    void eat() {
        System.out.println("animal : eat");
    }
}
class Cat extends Animal {
    void eat() {
        System.out.println("cat : eat");
    }
    void eatFish() {
        this.eat();   // this 调用自己的方法
        super.eat();  // super 调用父类方法
    }
}

调用结果如下:

animal : eat
cat : eat
animal : eat

注意:

子类的每个构造方法中均有默认的super(),调用父类的空参构造。手动调用父类构造会覆盖默认的super()。

super() 和 this() 都必须是在构造方法的第一行,所以不能同时出现。

 

2.6.2 构造器不能被继承

  • 子类不能继承父类的构造器(构造方法或者构造函数),它只是调用(隐式或显式)。因为子类有自己的构造器。值得注意的是子类可以继承父类的私有成员(成员变量,方法),只是子类无法直接访问而已,可以通过getter/setter方法访问父类的private成员变量。
  • 如果父类的构造器带有参数,则必须在子类的构造器中显式地通过super 关键字调用父类的构造器并配以适当的参数列表。
  • 如果父类构造器没有参数,则在子类的构造器中不需要使用 super 关键字调用父类构造器,系统会自动调用父类的无参构造器。

演示过程:

package com.nz;
public class InheritDemo3  {
    public static void main(String[] args) {
        System.out.println("------Teacher 类继承------");
        Teacher teacher = new Teacher();
        Teacher teacher2 = new Teacher("张三");
        System.out.println("------Student 类继承------");
        Student student = new Student();
        Student student2 = new Student("张三三");
    }
}
// 父类
class Person {
    private String name;
    Person(){
        System.out.println("调用了父类的无参构造器: Person()");
    }
    Person(String name) {
        System.out.println("调用了父类的带参构造器: Person(String name)");
        this.name = name;
    }
}
// Teacher子类继承Person
class Teacher extends Person{
    private String name;
    Teacher(){
        // 自动调用父类的无参数构造器 因为会有默认super();
        System.out.println("Teacher");
    }
    public Teacher(String name){
        super("太子爷哪吒");  // 调用父类中带有参数的构造器
        System.out.println("Teacher(String name):"+name);
        this.name = name;
    }
}
// Student子类继承Person
class Student extends Person{
    private String name;
    Student(){
        super("heihei");  // 调用父类中带有参数的构造器
        System.out.println("SubClass2");
    }
    public Student(String name){ // 自动调用父类的无参数构造器
        System.out.println("Student(String name):"+name);
        this.name = name;
    }
}

结果如下:

------Teacher 类继承------
调用了父类的无参构造器: Person()
Teacher
调用了父类的带参构造器: Person(String name)
Teacher(String name):张三
------Student 类继承------
调用了父类的带参构造器: Person(String name)
SubClass2
调用了父类的无参构造器: Person()
Student(String name):张三三

 

2.6.3 final修饰的类不能被继承

final 关键字主要用在三个地方:变量、方法、类。

  • 修饰类:表示该类不能被继承;
  • 修饰方法:表示方法不能被重写;
  • 修饰变量:表示变量只能一次赋值以后值不能被修改(常量)。

final 的特点:

  • 对于一个 final 变量,如果是基本数据类型的变量,则其数值一旦在初始 化之后便不能更改;如果是引用类型的变量,则在对其初始化之后便不 能再让其指向另一个对象。
  • 当用 final 修饰一个类时,表明这个类不能被继承。final 类中的所有成员 方法都会被隐式地指定为 final 方法。
  • 使用 final 方法的原因有两个。第一个原因是把方法锁定,以防任何继承 类修改它的含义;第二个原因是效率。在早期的 Java 实现版本中,会将 final 方法转为内嵌调用。但是如果方法过于庞大,可能看不到内嵌调用 带来的任何性能提升(现在的 Java 版本已经不需要使用 final方法进行这些优化了)。类中所有的 private 方法都隐式地指定为 final。

我们测试下修饰类后到底能不能继承:

package com.nz;
public class InheritDemo4 {
}
// 父类
final class Fu {
    private String name;
}
//class Zi extends Fu{ // Cannot inherit from final 'com.nz.Fu' 会显示没办法继承Fu
//}

结果:可以看出来在被final修饰的Fu类没办法继承,而且在编译期间就会报错了,没办法通过运行。

 

2.7 方法重写

 

2.7.1 介绍

子类中出现与父类一模一样的方法时(返回值类型,方法名和参数列表都相同),会出现覆盖效果,也称为重写或者复写。声明不变,重新实现。

 

2.7.2 使用场景与案例

发生在子父类之间的关系。
子类继承了父类的方法,但是子类觉得父类的这方法不足以满足自己的需求,子类重新写了一个与父类同名的方法,以便覆盖父类的该方法。

写个测试案例:

package com.nz;
public class InheritDemo5 {
    public static void main(String[] args) {
        // 创建子类对象
        Cat lanMao = new Cat();
        // 调用父类继承而来的方法
        lanMao.run();
        // 调用子类重写的方法
        lanMao.sing();
    }
}
class Animal{
    public void sing(){
        System.out.println("动物都可以唱歌!");
    }
    public void run(){
        System.out.println("动物都可以跑!");
    }
}
class Cat extends Animal {
    public void sing(){
        System.out.println("我们一起学猫叫,一起喵喵喵!让我们一起撒个娇");
    }
}

运行结果:

动物都可以跑!
我们一起学猫叫,一起喵喵喵!让我们一起撒个娇

可以看出,蓝猫调用了重写后的sing方法。

 

2.7.2 @Override重写注解

  • @Override:注解,重写注解校验!
  • 这个注解标记的方法,就说明这个方法必须是重写父类的方法,否则编译阶段报错。
  • 建议重写都加上这个注解,一方面可以提高代码的可读性,一方面可以防止重写出错!

加上后的子类代码形式如下:

class Cat extends Animal {
    // 声明不变,重新实现
    // 方法名称与父类全部一样,只是方法体中的功能重写了!
    @Override
    public void sing(){
        System.out.println("我们一起学猫叫,一起喵喵喵!让我们一起撒个娇");
    }
}

 

2.7.3 注意事项

  • 方法重写是发生在子父类之间的关系。
  • 子类方法覆盖父类方法,必须要保证权限大于等于父类权限。
  • 子类方法覆盖父类方法,返回值类型、函数名和参数列表都要一模一样。

 

总结

这篇文章就到这里了,如果这篇文章对你也有所帮助,希望您能多多关注三水点靠木的更多内容!

Java/Android 相关文章推荐
详解JAVA中的OPTIONAL
Jun 14 Java/Android
JPA如何使用entityManager执行SQL并指定返回类型
Jun 15 Java/Android
利用Java设置Word文本框中的文字旋转方向的实现方法
Jun 28 Java/Android
使用logback实现按自己的需求打印日志到自定义的文件里
Aug 30 Java/Android
logback 实现给变量指定默认值
Aug 30 Java/Android
spring cloud 配置中心客户端启动遇到的问题
Sep 25 Java/Android
springmvc直接不经过controller访问WEB-INF中的页面问题
Feb 24 Java/Android
springboot+zookeeper实现分布式锁
Mar 21 Java/Android
Java8利用Stream对列表进行去除重复的方法详解
Apr 14 Java/Android
Android 中的类文件和类加载器详情
Jun 05 Java/Android
springboot读取resources下文件的方式详解
Jun 21 Java/Android
Java多线程并发FutureTask使用详解
Jun 28 Java/Android
Java 泛型详解(超详细的java泛型方法解析)
SpringBoot集成Druid连接池连接MySQL8.0.11
Java使用httpRequest+Jsoup爬取红蓝球号码
使用Springboot实现健身房管理系统
Java Kafka 消费积压监控的示例代码
springboot临时文件存储目录配置方式
Jul 01 #Java/Android
Mybatis-plus在项目中的简单应用
Jul 01 #Java/Android
You might like
在PHP中使用反射技术的架构插件使用说明
2010/05/18 PHP
跟我学Laravel之安装Laravel
2014/10/15 PHP
php生成不重复随机数、数组的4种方法分享
2015/03/30 PHP
PHP的PDO预处理语句与存储过程
2019/01/27 PHP
Laravel Validator自定义错误返回提示消息并在前端展示
2019/05/09 PHP
JQuery 实现的页面滚动时浮动窗口控件
2009/07/10 Javascript
JavaScript window.document的属性、方法和事件小结
2012/10/24 Javascript
解决ExtJS在chrome或火狐中正常显示在ie中不显示的浏览器兼容问题
2013/01/11 Javascript
js实现可拖动DIV的方法
2013/12/17 Javascript
javascript间隔定时器(延时定时器)学习 间隔调用和延时调用
2014/01/13 Javascript
JavaScript遍历table表格中的某行某列并打印其值
2014/07/08 Javascript
教你如何使用node.js制作代理服务器
2014/11/26 Javascript
Js 正则表达式知识汇总
2014/12/02 Javascript
node.js中的fs.realpath方法使用说明
2014/12/16 Javascript
JavaScript如何调试有哪些建议和技巧附五款有用的调试工具
2015/10/28 Javascript
基于Bootstrap实现下拉菜单项和表单导航条(两个菜单项,一个下拉菜单和登录表单导航条)
2016/07/22 Javascript
jQuery图片轮播(二)利用构造函数和原型创建对象以实现继承
2016/12/06 Javascript
微信小程序 MD5加密登录密码详解及实例代码
2017/01/12 Javascript
jQuery动态添加.active 实现导航效果代码思路详解
2017/08/29 jQuery
vue 权限认证token的实现方法
2018/07/17 Javascript
微信小程序实现用table显示数据库反馈的多条数据功能示例
2019/05/07 Javascript
React组件设计模式之组合组件应用实例分析
2020/04/29 Javascript
详细分析Node.js 模块系统
2020/06/28 Javascript
[49:08]完美世界DOTA2联赛PWL S2 LBZS vs FTD.C 第一场 11.27
2020/12/01 DOTA
详解python开发环境搭建
2016/12/16 Python
Python2实现的LED大数字显示效果示例
2017/09/04 Python
Python3日期与时间戳转换的几种方法详解
2019/06/04 Python
Django MEDIA的配置及用法详解
2019/07/25 Python
Python通过2种方法输出带颜色字体
2020/03/02 Python
Python Pandas list列表数据列拆分成多行的方法实现
2020/12/14 Python
家长会开场白和结束语
2015/05/29 职场文书
小学语文国培研修日志
2015/11/13 职场文书
Python 详解通过Scrapy框架实现爬取百度新冠疫情数据流程
2021/11/11 Python
详解Vue的列表渲染
2021/11/20 Vue.js
使用 Docker Compose 构建复杂的多容器App
2022/04/30 Servers
SQL Server中锁的用法
2022/05/20 SQL Server