【JavaSE系列】Java面向对象之包与继承

⭐️前面的话⭐️

本篇文章带大家认识Java基础知识——包与继承,在Java当中一切皆可视为对象,而对象是由类所实例化出来的,将类组织起来那就是一个包,类与类之间是可以存在关联的,例如猫,狗,鸟等动物存在相同的行为或特征,我们把这些相同的行为与特征都集中起来构建成一个新的类,则猫,狗,鸟等动物都继承了该类。

?博客主页:未见花闻的博客主页
?欢迎关注?点赞?收藏⭐️留言?
?本文由未见花闻原创,CSDN首发!
?首发时间:?2021年11月20日?
✉️坚持和努力一定能换来诗与远方!
?参考书籍:?《Java核心技术》,?《Java编程思想》,?《Effective Java》
?参考在线编程网站:?牛客网?力扣
博主的码云gitee,平常博主写的程序代码都在里面。
博主的github,平常博主写的程序代码都在里面。
?作者水平很有限,如果发现错误,一定要及时告知作者哦!感谢感谢!



0


1.包

1.1概念

Java 允许使用包( package )将类组织起来。借助于包可以方便地组织自己的代码,并将自己的代码与别人提供的代码库分开管理。
标准的 Java 类库分布在多个包中,包括 java.lang、java.util 和java.net 等。标准的 Java包具有一个层次结构。如同硬盘的目录嵌套一样,也可以使用嵌套层次组织包。所有标准的Java 包都处于java 和 javax 包层次中。
使用包的主要原因是确保类名的唯一性。 假如两个程序员不约而同地建立了Employee类。只要将这些类放置在不同的包中, 就不会产生冲突。事实上,为了保证包名的绝对唯一性, Sun 公司建议将公司的因特网域名(这显然是独一无二的) 以逆序的形式作为包名,并且对于不同的项目使用不同的子包。例如, weijianhuawen.com 是一个的域名。逆序形式为 com.weijianhuawen。这个包还可以被进一步地划分成子包, 如 com.weijianhuawen.corejava
✨从编译器的角度来看, 嵌套的包之间没有任何关系。例如,java.util 包与java.util.jar 包毫无关系。每一个都拥有独立的类集合。

1.2类的组织

对类进行组织是由关键字package来设置类所在的包路径。

package 路径;
package com.csdn.test;

⭐️规则:

  1. .java文件最上面加上package语句指定该文件的代码所在的包。
  2. 包名尽量指定成唯一的名字,一般采用公司或单位的域名的颠倒形式。(如csdn.com,则包名com.csdn…)
  3. 包名要与路径相统一,如一个包名为com.csdn.test,则它所对应的路径为com/csdn/test
  4. 如果没有package包,则这些类会被放在一个默认包中。

包的创建:
1
不过在创建包前要注意设置一下编译器,这样编译器才能自动将你所写的包名根据.分开自动生成路径,否则有可能出现包名为com.csdn.test
2
包创建好后,创建一个类,编译器自动会生成语句package指定当前类所在的包。
3

1.3导入包中的类

在一个类中我们经常需要使用Java内置的一些方法,这个时候往往需要导包,比如以字符串输出数组的方法toString,我们需要导入java.util包中的Arrays类。
最原始的方法就是将类的具体路径写出来,再调用方法。

package com.csdn.test;

public class Package {
    public static void main(String[] args) {
        int[] arr = {1,2,3,4,5,6,7,8,9};
        String s = java.util.Arrays.toString(arr);
        System.out.println(s);
    }
}

4

除此之外,有一个关键字import能够导入一个包中具体的类。

package com.csdn.test;

import java.util.Arrays;//导入java.util包中的Arrays类

public class Package {
    public static void main(String[] args) {
        int[] arr = {1,2,3,4,5,6,7,8,9};
        System.out.println(Arrays.toString(arr));
    }
}

Arrays是直接点出toString方法说明该方法是静态的。
3
除了import java.util.Arrays导入包中的类,还可以使用import java.util.*,这个语句会导入java.util包中所有的类,但是不是直接将这个包中所有类全部导入,而是用到哪一个类就导入哪一个类。比如说只用Arrays类,实际上只会导入Arrays类,而这个*可以理解为通配符。
但是使用*导入类可能会存在一个问题,就是你导入两个或多个包,其中里面有两个包有同名的类,这个时候就会报错,因为编译器不知道你究竟需要导哪一个类。比如Date类,
5
6
这个时候要么写全路径访问,要么导包时具体导入所需要的那一个类。
如果你要得到一个时间戳,则导入包时语句需改为:

import java.util.Date;
import java.sql.*;
package com.csdn.test;
import java.util.Date;
import java.sql.*;

public class Import {
    public static void main(String[] args) {
        Date date = new Date();
        //获取时间戳
        System.out.println(date.getTime());
    }
}

7
如果不想改import语句,这需要写出类的全路径创建对象。

package com.csdn.test;
import java.util.*;
import java.sql.*;

public class Import {
    public static void main(String[] args) {
        java.util.Date date = new java.util.Date();
        //获取时间戳
        System.out.println(date.getTime());
    }
}

8
因为时间一直在变,所以时间戳也一直在变,所以不同时间运行程序结果是不同的。

如果导入类的类名与所在类类名相同,请把类名修改,否则会报错。

//被导入的类
package com.csdn.test;
public class Package {
    public int add(int a, int b) {
        return a + b;
    }
}

9

import com.csdn.test.Package;

public class Package2 {
    public static void main(String[] args) {
        Package p = new Package();
        int sum = p.add(2,3);
        System.out.println(sum);
    }
}

10

1.4静态导入

使用import static可以导入包中静态的方法或字段。
比如我们最常使用的打印方法:

package com.csdn.test;
import static java.lang.System.*;

public class Test {
    public static void main(String[] args) {
        String name = "未见花闻";
        out.println(name);
    }
}

11
还有数学运算方法Math包,里面的方法也是静态的。

package com.csdn.test;

import static java.lang.Math.*;

public class Test {
    public static void main(String[] args) {
        int a =2;
        double b = pow(a, 3);
        System.out.println(b);
        System.out.println(abs(-999));
        System.out.println(min(1, 5));
        System.out.println(max(122, 867));
    }
}

12

j

a

v

a

.

l

a

n

g

:

(

S

t

r

i

n

g

O

b

j

e

c

t

)

,

J

D

K

1.1

注意: java.lang:系统常用基础类(String、Object),此包从JDK1.1后自动导入。

java.lang:(StringObject),JDK1.1

1.5包访问权限

前面我们已经了解了

p

u

b

l

i

c

p

r

i

v

a

t

e

public和private

publicprivate这两个关键字分别表示公共与私有。但是一个类中的成员我们既没有说明public也没有说明private,没有任何访问权限的关键字修饰,则表示默认权限,即包访问权限,就是在同一个包内能够访问,不同包之间是不能访问的。

package com.csdn;

public class Demo2 {
    int val = 10;
}

在包外访问不到这个val:
13
包内访问:

package com.csdn;

public class Demo2 {
    int val = 10;

    public static void main(String[] args) {
        Demo2 d = new Demo2();
        System.out.println(d.val);
    }
}

14

2.继承

2.1什么是继承?

先来看一段代码:

class Dog {
    public String name;
    public int age;
    public Dog(String name, int age) {
        this.name = name;
        this.age = age;
    }
    public void eat() {
        System.out.println(name + "在吃饭!");
    }
    public void run() {
        System.out.println(name + "正在跑!");
    }
}

class Bird {
    public String name;
    public int age;
    public Bird(String name, int age) {
        this.name = name;
        this.age = age;
    }
    public void eat() {
        System.out.println(name + "正在吃饭!");
    }

    public void fly() {
        System.out.println(name + "正在飞!");
    }
}
public class Test1 {
    public static void main(String[] args) {
        Dog dog = new Dog("小狗", 18);
        dog.run();
        dog.eat();
        Bird bird = new Bird("小鸟", 19);
        bird.fly();
        bird.eat();
    }
}

15
我定义了两个类,一个是

D

o

g

Dog

Dog,另一个是

B

i

r

d

Bird

Bird,里面存放了两种动物的行为与特征,但是我们发现它们有不少共同点,比如它们都有名字,年龄,都会吃饭,这是所有动物都有的特征与行为,所以我们不妨将这些共同的特征封装成一个类

A

n

i

m

a

l

Animal

Animal,然后在定义

D

o

g

,

B

i

r

d

Dog,Bird

Dog,Bird类的时候后面加上extends Animal,这代表

D

o

g

,

B

i

r

d

Dog,Bird

Dog,Bird类继承了类

A

n

i

m

a

l

Animal

Animal

D

o

g

,

B

i

r

d

Dog,Bird

Dog,Bird类中不需要定义名字,年龄字段,吃饭的方法,就能使用这些成员变量和方法,这就叫做

继承

。其中

D

o

g

,

B

i

r

d

Dog,Bird

Dog,Bird为子类,

A

n

i

m

a

l

Animal

Animal类为父类。所谓继承,就是子类继承父类的字段和方法,private修饰的成员子类不能继承,或者说能继承,但是子类不能访问它,因为private的属性就是只能在类中进行访问,子类与父类不是同一个类,所以子类访问不了父类private修饰的成员。

class Animal {
    public String name;
    public int age;
    public void eat() {
        System.out.println(name + "在吃饭!");
    }
}
class Dog extends Animal{
    public Dog(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public void run() {
        System.out.println(name + "正在跑!");
    }
}

class Bird extends Animal{
    public Bird(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public void fly() {
        System.out.println(name + "正在飞!");
    }
}
public class Test1 {
    public static void main(String[] args) {
        Dog dog = new Dog("小狗", 18);
        dog.run();
        dog.eat();
        Bird bird = new Bird("小鸟", 19);
        bird.fly();
        bird.eat();
    }
}

16

2.2类的继承

前面用了一个实例引出了什么是继承,接下来我们来讨论一下继承的一些细节。

2.2.1继承的特征

继承的特征指的是对共性的抽取 ,使用

e

x

t

e

n

d

s

extends

extends关键字进行处理,能够对代码进行重复使用。

2.2.2Java中继承的规则

✨基本规则:

  1. Java中的继承是单继承,不能同时继承多个类(两个或两个以上),但是可以连续继承。
  2. 子类构造时,需 对父类帮助进行构造。
  3. s

    u

    p

    e

    r

    super

    super关键字表示父类对象的引用(不能出现在静态方法中),可以使用该关键字调用父类的构造方法与成员。

⭐️关于

s

u

p

e

r

super

super

  1. s

    u

    p

    e

    r

    (

    )

    super()

    super(),调用父类的构造方法。

  2. s

    u

    p

    e

    r

    .

    f

    u

    n

    c

    (

    )

    super.func()

    super.func(),调用父类的成员方法。

  3. s

    u

    p

    e

    r

    .

    d

    a

    t

    a

    super.data

    super.data,访问父类的成员变量。

  4. s

    u

    p

    e

    r

    (

    )

    super()

    super()在子类构造方法没有写,默认调用父类无参构造。

细心的同学已经发现了,上面所写的代码,并没有发现子类帮助父类进行构造,但是程序依然可以正常运行,其实当没有定义任何构造方法时,编译器会自动生成一个不带参数的构造方法,父类中没有定义构造方法,所以父类会生成一个不带参数的构造方法,子类的构造方法如果没有帮助父类调用构造方法时,相当于会自动生成不带参数的

s

u

p

e

r

(

)

super()

super(),所以程序依然可以正常运行。相当于下面的代码:

class Animal {
    public String name;
    public int age;
    public void eat() {
        System.out.println(name + "在吃饭!");
    }
}
class Dog extends Animal{
    public Dog(String name, int age) {
        super();
        this.name = name;
        this.age = age;
    }
    public void run() {
        System.out.println(name + "正在跑!");
    }
}

class Bird extends Animal{
    public Bird(String name, int age) {
        super();
        this.name = name;
        this.age = age;
    }
    public void fly() {
        System.out.println(name + "正在飞!");
    }
}

如果在父类中定义了一个带参数的构造方法,如果子类没有调用父类的构造方法,则会报错。
16
我们再来看一段代码:

class Animal {
    public String name;
    public int age;
    public Animal(String name, int age) {
        this.name = name;
        this.age = age;
    }
    public void eat() {
        System.out.println(name + "在吃饭!");
    }
}
class Dog extends Animal{
    public Dog(String name, int age) {
        super("动物", 1);
        this.name = name;
        this.age = age;
    }
    public void run() {
        System.out.println(name + "正在跑!");
    }
    public void fatherClass() {
        System.out.println(super.name + super.age);
    }
}
public class Test1 {
    public static void main(String[] args) {
        Dog dog = new Dog("小狗", 18);
        dog.fatherClass();
    }
}

✨这个程序会输出什么呢?

A

.

18

A.小狗18

A.18

B

.

1

B.动物1

B.1
你们觉得是哪个选项呢?我们了看看输出结果:
17
答案是A。为什么呢?首先帮助父类进行构造name = "动物" age = 1,然后子类在构造name = "小狗" age = 18,因为子类中没有name age,所以两者访问的都是父类对象的name age,所以输出小狗18。

当然如果子类中有name age,则会输出动物1。

class Animal {
    public String name;
    public int age;
    public Animal(String name, int age) {
        this.age = age;
        this.name = name;
    }
    public void eat() {
        System.out.println(name + "在吃饭!");
    }
}
class Dog extends Animal {
    public String name;
    public int age;
    public Dog(String name, int age) {
        super("动物", 1);
        this.name = name;
        this.age = age;
    }
    public void run() {
        System.out.println(name + "正在跑!");
    }
    public void fatherClass() {
        System.out.println(super.name + super.age);
    }
}

public class Test2 {
    public static void main(String[] args) {
        Dog dog = new Dog("小狗", 18);
        dog.fatherClass();
    }
}

18

2.3访问权限

除了public, private, default(包访问权限),还有一个protected访问权限关键字,范围是在default的基础是加上父子类。
19

20
我们希望类要尽量做到 “封装”, 即隐藏内部实现细节, 只暴露出 必要 的信息给类的调用者.因此我们在使用的时候应该尽可能的使用 比较严格 的访问权限. 例如如果一个方法能用 private, 就尽量不要用public.
另外, 还有一种 简单粗暴 的做法: 将所有的字段设为 private, 将所有的方法设为 public. 不过这种方式属于是对访问权限的滥用, 还是更希望同学们能写代码的时候认真思考, 该类提供的字段方法到底给 “谁” 使用(是类内部自己用, 还是类的调用者使用, 还是子类使用).

✨如果一个类不想被继承可以使用关键字final修饰类。
final修饰字段(变量),则变量只能初始化赋值一次,不能修改,相当于常量。
final修饰方法,则方法不能重写(下一篇博客详细介绍)。

2.4this与super区别

✨super来引用父类对象,用this来引用当前对象。

⭐️相同点:

  1. 均可以调用构造方法,但是两者不能同时出现在同一构造方法。
  2. 调用某构造方法时必须放在另一个构造方法第一行。
  3. 都不能放在静态方法中使用。

⭐️不同点:

  1. 引用对象不同。
  2. super()从子类中调用父类的构造方法,this()调用当前类的构造方法。

3.留给读者

下面代码输出什么?

class Animal {
    public String name;
    public int age;
    public Animal(String name, int age) {
        this.name = name;
        this.age = age;
    }
    public void eat() {
        System.out.println(name + "在吃饭!");
    }
}
class Dog extends Animal{
    public Dog(String name, int age) {
        super("动物", 1);
        this.name = name;
        this.age = age;
    }
    public void run() {
        System.out.println(name + "正在跑!");
    }
    public void fatherClass() {
        System.out.print(super.name + super.age);
    }
}

class Bird extends Animal{
    public Bird(String name, int age) {
        super("动物", 1);
        this.name = name;
        this.age = age;
    }
    public void fly() {
        System.out.println(name + "正在飞!");
    }
    public void fatherClass() {
        System.out.print(super.name + super.age);
    }
}
public class Test1 {
    public static void main(String[] args) {
        Dog dog = new Dog("小狗", 18);
        Bird bird = new Bird("小鸟", 19);
        dog.fatherClass();
        bird.fatherClass();
    }
}

A

.

18

19

A.小狗18小鸟19

A.1819

B

.

19

19

B.小鸟19小鸟19

B.1919

C

.

18

18

C.小狗18小狗18

C.1818

D

.

18

1

D.小狗18动物1

D.181
答案找博主,或者下篇博文见!

觉得文章写得不错的老铁们,点赞评论关注走一波!谢谢啦!

本图文内容来源于网友网络收集整理提供,作为学习参考使用,版权属于原作者。
THE END
分享
二维码
< <上一篇
下一篇>>