面向对象编程(包,继承,组合)

Posted Ischanged

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了面向对象编程(包,继承,组合)相关的知识,希望对你有一定的参考价值。


🎇🎆👀🎈

前言

面向对象编程的基本思想有继承,封装,多态,组合,从这篇博文开始我将陆续地写一些文章来和大家分享这些知识。(记录学习过程)

包含义

(package) 是组织类的一种方式,使用包的主要目的是保证类的唯一性.(包实际上在直观来看就是一个文件夹而已! !如我们在编写程序的时候创建了一个project项目,打开文件的存储目录,project里面有一个文件夹src就是一个包,我们打开目录就可以看到我们创建的java文件了,再如jar包里面包含的都是字节码文件,它们都是类文件)。
那么为什么要组织类呢????
假如在实际开发过程中,你在代码中写了一个 Test 类. 然后你的同事也可能写一个 Test 类. 如果出现两个同名的类, 就会冲突, 导致代码不能编译通过。因此我们引入了包,不同的包里面的类文件这样就可以同名了,就不会发生冲突了。

导入包中的类

要用哪个类我们直接导入相应的包就可以了,如要使用操作数组的类我们通过类的使用手册查看类对应的包和查看类里面的方法。**java 中已经提供了很多现成的类供我们使用,这些类已经封装好了在我们的JDK安装目录下的包里面,我们使用的时候导入相应的包,就可以使用这些类了,**这就是包的最大优势,不需要我们自己实现,,如果不导入是不能使用相应的类的,使用import关键字导入包,类似c语言的#include包含一下我们的头文件,头文件中包含我们使用的一些函数。如下面Scanner类,Array类的使用,我们就需要导入包,导入的包其实是以目录的形式展现的,通过“.”一层一层的包含目录,如import java.util.Arrays;就是导入JDK目录下java文件夹下的util文件夹下的Arrays.java文件。也可以写成impor java.util.+ '*'表示导入java.util文件夹下面所有的包,如操作数组的包,输入的包。*这个符号叫通配符,表示导入一个文件夹下的所有文件。

import java.util.Arrays;//导入数组包
import java.util.Scanner;
public class TestDemo {
    public static void main(String[] args) {
        Scanner scanner=new Scanner(System.in);
        int a= scanner.nextInt();
        System.out.println(a);
        int[] array = new int[10];
        System.out.println(Arrays.toString(array));

    }



}

当一个包中的类和另一个包中的类同名时,我们应该显式的指定要导入的类名. 否则是容易出现冲突的情况,如util 和 sql 中都存在一个 Date 这样的类, 你此时到底是要使用哪一个类呢?,不同的类是有区别, 此时就会出现歧义, 编译出错
冲突代码:

import java.util.*;
import java.sql.*;
public class Test {
public static void main(String[] args) {
// util 和 sql 中都存在一个 Date 这样的类, 此时就会出现歧义, 编译出错
Date date = new Date();
System.out.println(date.getTime());
}
}

在这种情况下需要使用完整的类名,就可以解决冲突了。

import java.util.*;
import java.sql.*;
public class Test {
public static void main(String[] args) {
java.util.Date date = new java.util.Date();//指定util包底下的Date类
System.out.println(date.getTime());
}
}

注意事项
import 和 C++ 的 #include 差别很大. C++ 必须 用#include 来引入其他文件内容, 引入的内容就包含在当前所在的文件了,但是 Java 有点不同,import 导入包时,并不会把包里面所有的文件都包含在当前文件里面,用到哪个类就包含哪个文件。这样文件不会太大。

静态导入

使用 import static 可以导入包中的静态的方法和字段.,这样可以使我们的代码更加简洁,**但有时过于简洁的代码的可读性降低了,让人看不懂,要思考很长时间才明白,**我们想想假如你写了这样的代码,给你的同事看,他要思索片刻才能明白,如果写了给向我一样的小白看,那我直接把写代码的人取关了(开玩笑哈)我们一般不这样写,简单了解即可。如下面的两段代码,**上面我们说可以通过import导入包之后就可以使用包里面的类了,在下面的代码里面就可以通过类名+“.”调用我们的方法了,如果我们导入的包的时候一直显示的写到类名,下面调用方法的时候就直接调用方法了,不需要通过类名调用了。**如下面的两段代码:

import static java.lang.System.*;//System也是类
public class Test {
public static void main(String[] args) {
out.println("hello");
}
}
import static java.lang.Math.*;
public class Test {
public static void main(String[] args) {
double x = 30;
double y = 40;
// double result = Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2));
// 静态导入的方式写起来更方便一些
double result = sqrt(pow(x, 2) + pow(y, 2));
System.out.println(result);
}
}

将类放到包中

上面简单的介绍了包,包其实就是让我们更好地管理我们的类,接下来我们来看如何管理我们的类,把类放入包中。
基本规则:
在文件的最上方加上一个 package 语句指定该代码在哪个包中(针对的是我们自己创建的包)
包名需要尽量指定成唯一的名字, 通常会用公司的域名的颠倒形式(例如 com.baidu.demo1 )并且小写,包名要和代码路径相匹配. 例如创建 com.bit.demo1 的包, 那么会存在一个对应的路径 com/bit/demo1 来存储代码.,如果一个类没有 package 语句, 则该类被放到一个默认包中.如下面我们建立一个com.baidu.demo的包,创建包的时候,不要建在工程目录下,要建在src目录下,idea在运行的时候首先是找src里面的文件,建立好包之后会出现三个文件夹com,baidu,demo它们之间是包含的关系的,我们在对应的目录下创建类,相应的类文件就存储在相应的目录下面和我们建文件的操作基本一样。
如下面大致演示一下建立一个com.baidu.demo的包,并且在baidu 包下面建了一个TestDemo类.

1.在 IDEA 中先新建一个包: 右键 src -> 新建 -> 包

2.在弹出的对话框中输入包名, 如com.baidu.demo

3.在包中创建类, 右键包名 -> 新建 -> 类, 然后输入类名TestDemo

创建好之后,我们既可以在idea里,baidu这个包的目录下看到我们创建的类,同时磁盘里存储代码的目录里面也多了这个类文件。对于自己建的包,在包里面新建类, 在新创建的 Test.java 文件的最上方, 就出现了一个 package 语句,使用package关键字,package后面跟上我们类所在目录的全路径,表示我们这个Test.java文件是哪个包底下的,

**对于在一个包里面使用另一个class类,如果当前的类的名称和使用类的名称相同,我们实例化对象的时候,编译器就会自动的在类的名称前面详细的说明当前这个类所在的包是哪一个,**如下图;


如果在一个类里面使用的是另一个不同名的类,我们进行实例化对象的时候,会使用import关键字导入我们使用的那个类所在的包,如图:

包的访问权限控制

在我之前的类和对象的博文中,我们已经了解了类中的 public 和 private. 的访问权限,private修饰的字段和方法只能在当前类里面使用,public修饰的字段和方法则可以在整个工程里面使用。包访问权限就是对于一个类里面的成员什么关键字都不加默认的就是包访问权限,包访问权限就是只能在同一个包(文件夹)里面的类才能进行访问,出了这个包就不能进行访问了
如下代码 TestDemo1 , TestDemo是 com.baidu.demo同一个包底下的两个类,com这个包底下又有一个 TestDemo同名的类,成员变量是包访问权限,同一个包底下的两个类可以进行访问,不同包底下的类不能进行访问。
在相同的包com.baidu.demo下面可以互相进行访问。在 TestDemo这个类里面实例化对象,通过对象去访问 TestDemo1类里面的成员变量。

package com.baidu.demo;
public class TestDemo1 {
    public int age;
    private String name;
    String sex;//这个属性  sex 他就是包访问权限-》 只能在当前包当中使用

}

package com.baidu.demo;

/**
 * Created by Terry
 * Description:
 * User: 14052
 * Date: 2021-09-15
 * Time: 11:50
 */
public class TestDemo {
    public static void main(String[] args) {
        TestDemo1 testDemo1 = new  TestDemo1();
        System.out.println( testDemo1.age);
        System.out.println(testDemo1.sex );
    }

}

在com包里面TestDemo 类中,实例化其他包中的类,访问类中的成员变量是不行的,在不同包中会报错。

package com;
import com.baidu.demo.TestDemo1;
public class TestDemo {
    public static void main(String[] args) {
        TestDemo1 testDemo1 = new  TestDemo1();
        System.out.println( testDemo1.age);
        System.out.println(testDemo1.sex );//这里会出现错误

    }
}

下面的类的权限也是包访问权限,访问不同包中的类也是不行的。

class TestDemo1 {
    public int age;
    private String name;
    String sex;//这个属性  sex 他就是包访问权限-》 只能在当前包当中使用

}

常见的系统包

  1. java.lang:系统常用基础类(String、Object),此包从JDK1.1后自动导入。
  2. java.lang.reflect:java 反射编程包;
  3. java.net:进行网络编程开发包。
  4. java.sql:进行数据库开发的支持包。
  5. java.util:是java提供的工具程序包。(集合类等) 非常重要
  6. java.io:I/O编程开发包。

继承

继承出现的背景

首先我们来简单的回顾下什么是类,什么是对象。类就是对一种事物的抽象,一种事物的一个模板,对一种事物的大致描述,通过这个模板就可以定义具体的对象了,对象就是以更加详细的实体。
在学习继承之前我们先来看一幅图:

上面的图片是一只猫和一条狗,我们可以用一个词来描述他们是什么呢?????,·那就是猫和狗都是“动物”,他们都具有动物的属性,有毛,有尾巴,会叫,会吃饭等,他们都继承了一些动物的属性,,他们有相似的地方也有不同的地方。如果用之前面向对象的思想来实现cat和dog这个类,那么我们的代码可以写成下面的样子:
Animal.java

package com.baidu.extenddemo1;
public class Animal {
    public String name;
    private int age;
    public void eat() {
        System.out.println(this.name+":吃饭饭!");
    }
    public void bark() {
        System.out.println(this.name+":叫!!!");
    }

}

Dog.java

package com.baidu.extenddemo1;
public class Dog   {
    public String name;
    private int age;
    public String color;
    public void eat() {
        System.out.println(this.name+":吃饭饭!");
    }
    public void bark() {
        System.out.println(this.name+":叫!!!");
    }
}

Cat.java

package com.baidu.extenddemo1;
public class Cat {
    public String name;
    private int age;
    public String color;
    public void eat() {
        System.out.println(this.name+":吃饭饭!");
    }
    public void bark() {
        System.out.println(this.name+":叫!!!");
    }
    public void upTree() {
        System.out.println(this.name+":上树!");
    }
}

上面的代码我们发现其中存在了大量的冗余代码,仔细分析, 我们发现 Animal 和 Cat 以及 Dog 这几个类中存在一定的关联关系:

  • 这三个类都具备一个相同的 eat 方法, 而且行为是完全一样的.
  • 这三个类都具备相同的 name,age 属性, 而且意义是完全一样的.
  • 从逻辑上讲, Cat 和 Dog都是一种 Animal (is - a 语义),

此时我们就可以让 Cat 和 Dog分别继承 Animal 类, 来达到代码重用的效果。此时, Animal 这样被继承的类, 我们称为 父类 , 基类 或 超类, 对于像 Cat 和 Bird 这样的类, 我们称为 子类, 派生类
和现实中的儿子继承父亲的财产类似, 儿子继承了父亲的财产,儿子就拥有所用的财产了,子类继承了父类就拥有了父类的所有属性和方法了。
下面的Cat,Dog类就可以进行简化了,Cat 和 Bird 继承自 Animal 类, 那么 Cat ,Dog在定义的时候就不必再写 和父类重复的字段和方法了。

public class  Cat extends Animal {

    public String color;
    public void upTree() {
        System.out.println(this.name+":上树!");
    }
}

  public class Dog  extends Animal {
}

语法规则

基本语法样式

class 子类 extends 父类 { }

  • 使用 extends 指定父类.
  • Java 中一个子类只能继承一个父类 (而C++/Python等语言支持多继承).
  • 子类会继承父类的所有 public 的字段和方法.
  • 对于父类的 private 的字段和方法, 子类中是无法访问的.
  • 子类的实例中, 也包含着父类的实例.可以使用 super 关键字得到父类实例的引用.

对于最后的一条语法规则有点难理解我们来具体看一下,它的意思就是对于子类继承了父类,要先显示的帮助父调用构造方法,进行实例化产生对象,在继承父类之前,就要有父类这个对象了,才能继承,否则就会出现编译错误。之前在类和对象那里我们已经知道了,在进行类的实例化的时候,如果没有提供带有参数的构造方法,编译器会默认提供一个不带参数的构造,有了带有参数的构造方法则编译器不再提供默认的构造方法
如下代码:

class Animal1 {
public String name;
    public int age;
    public void eat() {
        System.out.println(this.name+":吃饭饭!");
    }
    public void bark() {
        System.out.println(this.name+":叫!!!");
    }
}
//==============================================
class Cat1 extends Animal1{
   /* public Cat1() {
        super();//显示的调用父类的构造方法  以前没有构造方法编译器  自己会默认生成
    }
*/
    public void upTree() {
        
        System.out.println(this.name+":上树!");
    }


public class Test2 {
    public static void main(String[] args) {
        Animal1 animal1=new Animal1();
        animal1.eat();
    }

在继承时实例化子类对象的时候,调用带有参数的构造方法,在实例化子类对象之前,一定要使用super关键字调用父类的构造方法进行构造。

class Animal1 {
public String name;
    public int age;

    public Animal1(String name) {
        this.name = name;
    }
    public void eat() {
        System.out.println(this.name+":吃饭饭!");
    }
    public void bark() {
        System.out.println(this.name+":叫!!!");
    }
//
class Cat1 extends Animal1{
    public Cat1(String name) {
        super(name);//显示的调用父类的构造方法,在子类没有构造完之前就进行构造,括号里面的语句都还没执行完 
    }
}

public class Test2 {
    public static void main(String[] args) {
        Cat1 cat1 = new Cat1("haha");//调用子类的构造方法
        cat1.upTree();
    }

字段和方法的四种访问权限

四种访问权限的作用范围如下:

范围privatedefaultprotectedpublic
同一包中的同一类
同一包中的不同一类和子类
不同包中的子类
不同包中的非子类

之前写代码,我们大部分用public修饰我们的成员变量和方法,被public修饰的成员变量和方法的访问范围很大可以在整个工程里面使用,有时我们不希望它在整个工程里面被使用,不希望其他类的使用者,修改这个类里面的成员变量和方法,那我们可以用private修饰,但private修饰之后它的访问权限太低了只能在同一包中的同一类,那么比较适合的就是字段和方法用protected修饰,它的访问范围比较适中。
从表格可以看出protected,比较特别不同的一点就是,不同包中的子类可以访问这一点了,如下面的代码:
com.baidu.extenddemo1包下面的的Animal.java文件

package com.baidu.extenddemo1;
public class Animal {
    protected String name="haha";


    private int age;
    public void eat() {
        System.out.println(this.name+":吃饭饭!");
    }
    public void bark() {
        System.out.println(this.name+":叫!!!");
    }

}

src下的文件

public class TestDemo extends Animal {
    public void func()
    {
        Animal animal = new Animal();
        System.out.println(super.name); //protected 修饰的关键字不同包底下,只能通过super关键字访问我们父类里面的name;不能通过对象的引用访问
    }
        public static void main(String[] args) {

          //  System.out.println(super.name); //只能通过super关键字访问,我们父类里面的name;也不能写在main函数里面,super不依赖于对象

    }
}

另外当子类的字段和父类的字段重名的时候什么时候使用哪一个要看代码,在子类里面什么关键字都不引用或者通过this关键字访问同名的字段,默认是调用子类里面近的那个字段,使用super关键字可以访问同名的父类字段。下面代码的结果就是3,3,1。

class Base {
    public int a=1;
    public int b=2;
}
class Derieve extends Base{
    public int a=3;
    public void func() {
        System.out.println("func(),函数被调用");
        System.out.println(a);
        System.out.println(this.a);
        System.out.println(super.a);
    }
}
public class Test {
    public static void main(String[] args) {
        Derieve derieve = new Derieve();
        derieve.func();
    }
}

小结: Java 中对于字段和方法共有四种访问权限

  • private: 类内部能访问, 类外部不能访问
  • 默认(也叫包访问权限): 类内部能访问, 同一个包中的类可以访问, 其他类不能访问.
  • protected: 类内部能访问, 同一包中的子类和同一个包中的不同类可以访问, 其他类不能访问.
  • public : 类内部和类的调用者都能访问

如何正确使用访问权限????
我们希望类要尽量做到 “封装”, 即隐藏内部实现细节, 只暴露出 必要 的信息给类的调用者,因此我们在使用的时候应该尽可能的使用 比较严格 的访问权限. 例如如果一个方法能用 private, 就尽量不要用public,另外写代码的时候要认真思考, 该类提供的字段方法到底给 “谁” 使用(是类内部自己用, 还是类的调用者使用, 还是子类使用)

更复杂的继承关系

刚才我们的例子中, 只涉及到 Animal, Cat 和 Bird 三种类. 但是如果情况更复杂一些呢?如针对 Cat 这种情况, 我们可能还需要表示出更多种类的猫,如图:

这个时候使用继承方式来表示, 就会涉及到更复杂的体系.

// Animal.java
public Animal {
...
}
// Cat.java
public Cat extends Animal {
...
}
// ChineseGardenCat.java
public ChineseGardenCat extends Cat {
面向对象编程(包,继承,组合)

面向对象编程(包,继承,组合)

Python(面向对象编程——2 继承派生组合抽象类)

Java万物皆对象——面向对象编程

面向对象概论之继承简述,组合简述,菱形继承简述

继承和组合