目录
前言
在Java初阶之包的使用与继承学习中,我通过总结以及查阅资料,对以下问题有了更深一层的理解。
- 访问修饰符有哪些?他们的权限大小是如何排序的?
- 什么是包?常见的包有哪些?包是如何导入的?什么是包访问权限?
- 继承是如何实现的?继承的使用规则有哪些?
- 如何进行父类属性的访问?在子类中如何调用父类方法?
- 父子类对象如何产生?如何调用父类的构造方法?
- 组合与继承的典型表现是什么?
- final的用法有哪些?
如果你在Java初阶之包的使用与继承的学习中,也想对这些问题有一个更深的了解,请阅读这篇文章,或许会对你有所启发。
一、包的使用
1.访问修饰符
在Java中一共有四个关于权限的关键字,依从权限从小到大为private<default<protected<public
public//公共的
protected//受保护的
default//默认的
private//私有的
- private是私有访问权限,仅在当前类的内部可见,是一种封装的体现
- default是默认访问权限,同一个包中的类可以访问,什么时没有加修饰符,认为是friendly
- protected是继承访问权限,在不同包(和父类不同包)的子类可见
- public是公共访问权限,可以被所有其他类所访问
2.包
包其实就是操作系统的一个文件夹,包访问权限,就是访问权限什么也不需要写,只有在当前这一级文件夹的内部可见,正因为有包访问权限的存在,可以在不同的包中创建同名的类,类似在操作系统的同一个文件夹内不能创建多个同名同类型的文件,但是在不同文件夹中,可以创建多个同名称同类型的文件,在Java中类的全名称是包名,类名,一个类使用package关键字来声明该类属于哪个包下。
JDK中常见的包有:
- java.lang:系统常用的基础类,String,Object,Math
- java.lang.reflext:反射开发包
- java.util:java提供的工具开发包(所有集合类ArrayList,LinkedList,HashMap等)
- java.sql:java数据库开发相关的包
- java.io:IO开发包(文件的操作)
3.包的导入
在Java中提供了很多现成的类供开发者使用,例如Data类,日期类,在java.util包下有Data类,在java.sql包下也有Data类,因此我们在具体使用的时候就要使用全名称,可以使用关键字import告知编译器导入包,假设我们现在在当前类中还需要使用java.util这个包中的其他类我们就可以用通配符*来表示导入,也就是import java.util.*导入util包,需要哪个就导入哪个,需要我们注意的是import关键字表示在java中导入某个包中的某个具体的类,无法直接导入一个文件夹的。
4.包访问权限
包访问权限是指在同一个包中可见,仅限于当前这个包中的所有类之间可见,其他包包括子包都不行。
代码展示:
public class Person{
String name;
int age;
void show(){
System.out.println();
}
}
下图中,Person和Test都属于同一级包,因此Person中的包访问权限对于Test类是可见的。
二、继承
1.继承的实现
将若干个不同的类之间相同的属性和方法抽象为一个共同的父类,子类只需要扩展自己类中独有的属性和方法即可,把这种编程特性,称之为“继承”,要想使用继承,能发生继承的类之间必须满足is a原则,比如Dog is an Animal,Cat is an Animal。在Java中使用extends表示继承一个父类,父类中的所有属性和行为都会被子类继承下来,子类只需要关心实现自己独有的属性和行为即可,大大减少了重复代码,实现了代码的复用,扩展新的子类就更加容易。
2.继承的使用规则
在Java中,类的继承是单继承,一个类智能使用extends关键字继承一个父类,Java不允许多重继承,但是允许多层继承,一般多层继承最多不超过3层关系。
错误代码展示:
正确代码展示:
class A {
int a;
}
class B extends A{
int b;
}
public class C extends B{
public static void main(String[] args) {
C c = new C();
System.out.println(c.a);
System.out.println(c.b);
}
}
关于继承方面的隐式继承和显示继承,隐式继承就是父类中的某些属性和方法,子类继承下来了,但是无法直接使用,显示继承就可以随时使用。
3.访问父类的属性
子类要想直接访问父类的属性,父类中的属性权限必须大于等于protected的访问权限,若继承是显示继承,使用父类的属性,可以直接使用,此时必须是子类中没有和父类同名的属性,那若子类也定义了父类相同名称的属性呢?此时就又要用到我们之前提到的程序开发的就近匹配原则,编译器会率先在方法中寻找,再在当前类的内部寻找变量,也就是在子类中寻找,最后去父类中寻找同名变量,这时我们可以发现,父类中的同名变量就被子类覆盖了,那若是父类和子类变量名称相同,但是类型不同,访问的又该是什么呢?其实只要在子类中定义了和父类名称相同的属性,无关类型使用相同名称时,调用的都是子类中覆盖后的变量!
代码展示:
public class Derived extends Base{
int a;
int c;
int d;
public static void main(String[] args) {
Derived derived=new Derived();
derived.b=20;
derived.c=30;
derived.d=40;
//到底访问的是子类中的a属性还是父类中的a属性呢?
System.out.println(derived.a);
}
}
如果要想在子类中调用被覆盖的父类中同名属性,需使用super关键字,super是用来修饰属性的,明确表示直接从父类中寻找同名属性,表达式为super.
代码展示:
public class Derived extends Base{
int a;
int c;
int d;
public static void main(String[] args) {
Derived derived=new Derived();
derived.test();
}
public void test(){
//调用子类中的a属性
System.out.println(a);
//调用父类中被覆盖的属性a,使用super关键字
System.out.println(super.a);
}
}
4.在子类中调用父类的方法
public class Derived extends Base{
int a;
int c;
int d;
public static void main(String[] args) {
Derived derived=new Derived();
derived.testA();
derived.testB();
}
public void testB(){
System.out.println("子类中的testB方法");
}
}
从上述代码中我们可以看到testA在子类中不存在,继续去父类中寻找同名方法,若子类定义了和父类名称完全相同的方法呢?此时就要提出一个新的名词,叫方法重写,方法重写英文表示override,是指发生在有继承关系的类之间,子类定义了和父类除了权限不同以外其他全部相同,包括名称,参数列表,返回值都相同的方法,我们要保证的是子类重写后的方法权限大于等于父类的权限,要想在子类中调用被覆写后的父类方法,使用super.方法名称()表示直接从父类中寻找同名方法。
5.父子类对象的产生
当调用子类构造方法产生子类对象时,JVM首先调用父类的构造方法先产生父类对象而后产生子类对象。
代码展示:
public class Derived extends Base{
int a;
int c;
int d;
public Derived(){
//显示使用super表示调用父类的有参构造
super('a');
System.out.println("子类Derived的构造方法");
}
}
结果展示:
调用父类的构造方法要使用 super关键字,若调用的是父类的无参构造,super可以不写;若调用的是父类的有参构造,则必须在参数列表显示使用super,明确表示先调用父类的构造方法。在子类中我们既可以使用super关键字来调用父类的构造方法,也可以用this关键字进行调用,调用父类构造方法时,super必须放在子类构造方法的首行,this的构造方法调用也得放首行,this和super表示构造方法调用时,不能同时出现。
super调用错误代码展示:
public class Derived extends Base{
int a;
int c;
int d;
public Derived(){
this(10);
//显示使用super表示调用父类的有参构造
super('a');
System.out.println("子类Derived的构造方法");
}
结果展示:
this调用错误代码展示:
public class Derived extends Base{
int a;
int c;
int d;
public Derived(){
//显示使用super表示调用父类的有参构造
super('a');
this(10);
System.out.println("子类Derived的构造方法");
}
结果展示:
this可以表示当前对象的引用,但是super没有这个特点,只能引用父类的属性或方法,super在引用父类构造方法时必须放首行,super和this都表示成员域的属性,它们有一个共同的特点是都不能在静态域中直接使用。
三、组合与继承
组合与继承讲的其实是类和类之间的关系,学生类和人类就是典型的继承关系,继承满足is a原则,继承关系的类之间是个树状结构。
代码展示:
class Person{
public void sleep(){
System.out.println("正在呼呼大睡");
}
public void eat(){
System.out.println("正在大口大口吃");
}
}
class Student extends Person{
@Override
public void eat(){
System.out.println("正在大口大口吃");
}
public static void main(String[] args) {
Student s=new Student();
s.sleep();
s.eat();
}
}
说到组合,学生类和学校类就是最典型的组合关系,组合满足has a原则。
代码展示:
class School{
public void school(){
System.out.println("school");
}
}
class Student{
public void student(){
System.out.println("student");
}
}
class Study{
public void study(){
new School().school();
new Student().student();
}
}
public class Test{
public static void main(String[] args) {
new Study().study();
}
}
四、final的用法
1.final修饰属性
final表示终极器,到此为止的意思,final修饰的属性,值无法修改,使用final关键字来定义常量。
- final修饰基本数据类型,值不能修改,数值在定义后无法修改
final int a=10;
System.out.println(a);
a=20;
System.out.println(a);
//其中a表示一个常量,定义后值无法修改,只能保存10
- final修饰引用数据类型,值不能改,这个值指的是引用保存的地址不能改,但是地址中包含的内容依旧可以修改。
//final修饰引用数据类型,引用指向的地址值不能修改
//arr引用保存的地址值不能修改
//此时arr只能指向这个数组
final int[] arr=new int[3];
arr[0]=10;
arr[1]=20;
System.out.println(Arrays.toString(arr));
int[] arr1=new int[10];
arr=arr1;
2.被final修饰的方法不能被重写
当使用final修饰方法时,这个方法将成为最终方法,不允许被子类覆盖,但是该方法仍然可以被继承。
代码展示:
class Aniaml {
public final void bark(){
System.out.println("叫");
}
public void eat(){
System.out.println("吃");
}
}
class Dog extends Animal{
//final修饰的方法不能被重写,但此方法仍然可以被继承
public void eat(){
System.out.println("狗在吃骨头");
}
}
public class Test{
public static void main(String[] args) {
Dog d=new Dog();
d.bark();
}
}
3.被final修饰的类不能被继承
被final修饰的类不能被继承意味着final修饰的类没有子类,JDK中的String类就是一个典型的final,类,那为何String类要使用final修饰?这是保证JDK的使用者,大家用到的String类完全一模一样,没有任何别的版本。
总结
以上就是今天所讲内容,希望大家都能熟练掌握!