- 一、关于访问修饰符
- 1.1关于访问修饰符从小到大的顺序依次
- 1.2关于包
- 1.3包存在的意义是什么
- 1.4如何导入包中的某个类
- 1.5静态导入(了解即可)
- 1.6几种常见的系统包
- 二、继承
- 2.1定义
- 2.2继承规则
- 2.3隐式继承(private属性和方法)
- 三.关于protected访问权限(重点,易错点)
- 3.1不在一个包中且不是一个子类无法访问
- 3.2继承子类可以使用protected属性
- 3.3练习(重点)
- 3.4final关键字(重点)
- 四.super关键字
- 4.1this关键字用法
- 4.2 super关键字用法
- 15年校招笔试题关于继承+构造方法+代码块调用问题
- 4.3 super关键字修饰属性
- 4.4 super修饰构造方法
- 4.5 super修饰普通方法
- 五.多态
- 5.1向上转型
- 5.2方法重写
- 5.3发生重写时,权限问题
- 注解Override
- 5.4向上转型的时机
- 5.5向上转型
- 5.6向下转型
- 六.抽象类
- 6.1abstruct关键字
- 6.2抽象类作用
- 七.接口
- 7.1接口使用的场景
- 7.2接口使用方法
- 7.3接口表示能力
- 7.4接口和类的关系
- 八.Java中万物之母Object类
- 8.1任意类型都可以发生向上转型成为Object类
- 8.2Object类中包含toString方法
- 8.3Object类中的equal方法(重点)
- 8.4Object类最高参数统一化
- 九.内置常用接口
- 9.1java.lang.Comparable比较接口
- 覆写compareTo方法(必须掌握)
- 9.2java.lang.Cloneable克隆接口
- 十.深浅拷贝(了解即可)
- 总结
一、关于访问修饰符 1.1关于访问修饰符从小到大的顺序依次
private(私有,当前类的内部可见) 使用包的主要目的是保证类的唯一性. 比如util和sql都有date类,编译器不知道该匹配那一个类 例如:当导入System这个类之后,可以直接out.println(123) 很多时候写代码会出现代码冗余的情况该如何解决? 代码如下(示例): a.要能使用继承,前提必须满足类之间的is a关系b.—个子类只能使用extends探承—个父类。(单继承) b.Java中不允许多重继承。extends后面只能跟一个父类,允许多层继承,没法当儿子,可以当孙子。 子类其实也继承了这个属性和方法,但是无法直接在子类中使用。代码示例如下: 切记:同包下的没有关系的类之间以及不同包的有继承关系的类之间可见的!!!!!(死记!!!!!) 解决上述问题的办法:将test类移动到父类一个包中,因为name属性是protected,相同的包下,没有继承关系的类可以使用protected。但是子包下是不可以使用的,只可以在同级目录。 代码示例如下 曾经我们学习过 final 关键字, 修饰一个变量或者字段的时候, 表示 常量 (不能修改). final 关键字也能修饰类, 此时表示被修饰的类就不能被继承. 我们平时是用的 String 字符串类, 就是用 final 修饰的, 不能被继承 请问输出顺序是什么? 答案是:36 7 2154 2154 8 super修饰属性表示从父类中寻找同名属性 注意:如果此时将父类name属性设置为private是访问不到的。 但也存在例外: China类要产生子类对象就会默认先产生“父辈类们”对象(套娃) 解决方法: 总结(重点) 定义:一个引用可以表现出的多种行为和特性 定义:发生在有继承关系的类之间,除了子类和父类定义的权限不同,其他全部(方法名称,返回值,参数列表)都相同的方法。 普通方法可以重写, static 修饰的静态方法不能重写. 解析:首先主方法new了一个子类student,但会先创建一个父类对象,在父类中调用fun方法,但父类中this指代父类中的test方法,test方法权限是private无法被覆写,所以打印1 编译时就会报错,不会在运行时报错 1:方法传参 此外,如果在new D()下面在调用d.fun()时,num=10了,因为上一次打印是在fun覆写方法内部,还没有给num赋值。此时 D 对象自身还没有构造, 此时 num 处在未初始化的状态, 值为 0 目的:想用子类中拓展的方法是,但是这个方法在父类中没有。 由来: 没有方法体指的是"{}" 抽象类中可以包含其他的非抽象方法, 也可以包含字段. 这个非抽象方法和普通方法的规则都是一样的, 可以被重写,也可以被子类直接调用 关于abstruct和final的两个思考 抽象类存在的最大意义就是为了被继承. 有些同学可能会说了, 普通的类也可以被继承呀, 普通的方法也可以被重写呀, 为啥非得用抽象类和抽象方法呢? 使用抽象类的场景就如上面的代码, 实际工作不应该由父类完成, 而应由子类完成. 那么此时如果不小心误用成父类了,使用普通类编译器是不会报错的. 但是父类是抽象类就会在实例化的时候提示错误, 让我们尽早发现问题. 接口是抽象类的更进一步. 抽象类中还可以包含非抽象方法, 和字段. 而接口中包含的方法都是抽象方法, 字段只能包含静态常量 接口不同于抽象类,存在is a的关系,抽象类中子类和抽象父类仍然存在继承树的关系,抽象父类中的方法,子类必须有继承关系才可以使用,接口就不用。 有的时候我们需要让一个类同时继承多个父类. 这件事情在有些编程语言通过 多继承 的方式来实现的. 此时usb相当于一种规范 接口多继承,用","分割 接口之间是可以使用继承挂系的 可以查看Object类源代码:ctrl+鼠标左键 如果new的两个对象,即使属性值完全相同,用“==”比较返回值也是false,因为new的两个对象存在不同的地址! 1.this==obj相当于判断两个引用类型是不是相同的地址,判断是不是自己和自己比较,直接返回true。 因此在Java中,若一个方法参数或者返回值是Object类型,说明该参数或者返回值可以是任意引用数据类型(数组,类,接口) 当一个类实现了Comparable接口,表示该类具备了可比较的能力!! 能使用compareTo方法主要是因为该类在声明时impl了一个comparable接口,在该类中覆写comparable接口中compareTo方法(子类具备该能力)。 为什么比较之后就可以排序的原因:Arrays.sort()方法的内部实际上就是根据Compable接口的compareTo方法的返回值进行比较的。(下图为自己写的排序。冒泡排序)这个compareTo方法是定义在comparable接口中的。只要一个类实现了comparable接口都可以实现compareTo方法排序 但其实在这个接口中我们查看其原码,会发现接口中没有任何抽象方法和全局常量。 核心区别: 抽象类中可以包含普通方法和普通字段, 这样的普通方法和字段可以被子类直接使用(不必重写), 而接口中不能包含普通方法, 子类必须重写所有的抽象方法. 再次提醒:
例如, 你在代码中写了一个 Test 类. 然后你的同事也可能写一个 Test 类. 如果出现两个同名的类, 就会冲突, 导致代码不能编译通过
下面的Test类和上面的Test类是两个完全不同的类
但是" . "有时候会存在歧义*
遇到这种情况该如何解决?
二、继承
2.1定义
这三个类都具备一个相同的 eat 方法, 而且行为是完全一样的.
这三个类都具备一个相同的 name 属性, 而且意义是完全一样的.
从逻辑上讲, Cat 和 Bird 都是一种 Animal (is - a 语义).
此时我们就可以让 Cat 和 Bird 分别继承 Animal 类, 来达到代码重用的效果
2.2继承规则
关于代码冗余解决方案:
此时, Animal 这样被继承的类, 我们称为 父类 , 基类 或 超类, 对于像 Cat 和 Bird 这样的类, 我们称为 子类, 派生类
和现实中的儿子继承父亲的财产类似, 子类也会继承父类的字段和方法, 以达到代码重用的效果
此时如果Taidi继承了animal相当于也是Dog类的子类
c.子类会继承父类的所有属性和方法,显示继承(public属性和方法可以直接使用)
d.使用 extends 指定父类.
e.子类会继承父类的所有 public 的字段和方法.对于父类的 private 的字段和方法, 子类中是无法访问的.
f.子类的实例中, 也包含着父类的实例. 可以使用 super 关键字得到父类实例的引用
使用get和set的方法解决此类问题。
补充:静态的属性和方法是归于某个类所有,当一个类继承了另一个类,肯定所有静态属性和方法就继承。到底能否直接使用,还是要看权限是不是public权限
三.关于protected访问权限(重点,易错点)
先将animal类中name属性设置为protected属性(下面所有问题基于上图分析)
但是如果出现以下的情况不可以使用!
person和animal两个类在分别不同的包中,test类和person类在一个包中。虽然person类继承了animal的属性,但是当前是在test这个类中调用。protected指的是不同包的具有继承关系的类的内部可见的。
如果在子类中使用父类引用创建父类对象使用name就不可以,因为已经出了父类。(属于语法,记住即可)
final int a = 10;
a = 20; // 编译出错
final public class Animal {
...
}
public class Bird extends Animal {
...
}
// 编译出错
Error:(3, 27) java: 无法从最终com.bit.Animal进行继承
要产生一个子类对象,就会先产生一个父类对象。比如在主类方法中new一个子类对象时
package animal;
public class B {
public B() {
System.out.println("1.B的构造方法---------------");
}
{
System.out.println("2.B的构造块-----------------");
}
static {
System.out.println("3.B的静态块-----------------");
}
}
package animal;
public class D extends B{
public D() {
System.out.println("4.D的构造方法--------------");
}
{
System.out.println("5.D的构造块----------------");
}
static {
System.out.println("6.D的静态块----------------");
}
public static void main(String[] args) {
System.out.println("7.main开始。。。。");
new D();
new D();
System.out.println("8.main结束。。。。");
}
}
解析:
当前类china类中没有name属性,所以this关键字首先默认优先在当前类中查找name属性,没有name属性再去父类person类寻找同名name属性。
若person类中也没有那么属性,继续向上animal类中查找(person类继承了animal类)
如果person类和ainimal类都有name属性,在china类中使用super关键字是先找到直接父类person类中的name属性而不是“爷爷类”animal类。
super关键字向上访问遇到private就不会继续在向上访问了(比如图示在china类中super.name就直接没有权限访问父类name,找到了属性但是没有权限访问)
当只有两个类时,在china类中new一个新对象,而china类中只有默认的无参构造,父类中person有一个有参构造,则默认的无参构造方法不会产生。
下图是China类属于Person类子类
其实在public china无参构造方法下默认调用了一下super()
在使用子类无参构造方法时先调用父类有参构造方法
或者可以只写this(),因为编译器会默认加上super(),但是自己写上就不可以!!!!
必须显示使用super有参构造方法
在一个构造方法中无法同时使用this和super关键字,因为this调用构造方法也必须放在首行
只要使用了super关键字去调用父类的构造方法就不能再使用this关键字调用本类的无参构造
注意this和super区别:可以访问父类的属性和方法但是不能直接使用super必须是super.东西。除了构造方法以外
最主要的多态:继承+方法重写
鸭类是鸟类的子类,鸟类又是动物的子类。new的不一定是子类也可能是子子孙孙类
向上转型最大的意义在于参数统一化,降低使用者的使用难度。具体示例代码如下
有了向上转型之后,通过一个父类引用就可以指代所有子类对象(参数统一化)
并且,即使新创造一个子类,也可以直接调用
多态性的体现:同一个引用(变量名称),同一个方法名称,但是根据对象不同,表现出不同的行为。
判断调用方法的规则:
若子类没有重写eat这个方法,调用的一定是父类的方法。还要遵循就近匹配原则。比如duck类是bird类子类,bird类是animal子类,当方法重写中duck没有重写eat方法,调用fun(animal2)时,调用的不是animal的eat方法,而是直接父类bird类的eat方法
在 Java 中, 调用某个类的方法, 究竟执行了哪段代码 (是父类方法的代码还是子类方法的代码) , 要看究竟这个引用指向的是父类对象还是子类对象
比如以下的情况,父类中animal定义的是protected权限,子类是default权限,编译报错。
但是也存在例外,private不包含在内,private方法无法被覆写。父类中的私有方法无法被子类直接使用
父类animal中重写privat的eat方法,在test类(注意与解决方法中的区别)中测试:
解决方案:子类都使用public修饰eat方法,把主方法换到animal这个类中来调用,调用的都是animal的eat方法。因为这是一个私有类方法,出了这个类根本用不了。
练习:此时打印的是1还是2?class Person {
public void fun() {
this.test();
}
private void test() {
System.out.println("1.Person的test方法");
}
}
class Student extends Person {
public void test() {
System.out.println("2.Student的test方法");
}
}
public class Test {
public static void main(String[] args) {
new Student().fun();
}
}
如果改变父类中test权限后:
关于方法重写返回值完全相同的问题:
除了向上转型:student本质就是person的子类
但是在子类student中重写返回值为person类的方法是不可取的,父类可能还包含其他子类,返回的范围大于子类(可以理解为以下犯上):
还需要注意的一个易错点,不能重写static方法,static方法与对象无关
方法重写只能用于普通方法
总结:
2:引用赋值
3:方法返回值
返回类型是父类animal,return回来的是一个子类的对象
校招笔试题(重难点 ):
解析:首先,new D()时,因为存在继承关系所以会先调用父类B类构造方法产生B类对象,在调用调用父类B类中的fun方法,但是子类D中存在方法重写,也有fun方法,并且new的也是子类对象。所以此时直接调用子类D的fun方法(子类覆写方法),但是此时还没有进入D的构造方法,num为默认值0。(构造方法用于给对象中的属性赋值)
不能调用".“play方法的原因:能”.“什么方法,”.“前面的内容说了算,具体方法实现是什么样子,”."后面的内容说了算。啥时候发生向上转型:方法接收一个类和当前类的子类,参数指定为相应的父类引用,发生的就是向上转型
仍然想调用子类拓展方法的解决办法:animal.paly(向下转向)
animal这个类引用本质上还是一个Dog类。子类变成父类一定要强制类型转换。
思考:这里产生几个对象?一个。
只new了一次,关键看new的次数。只是这里的对象一开始被animal引用,后来animal引用更改名字变成dog
此时animal类和dog类毫无关系,发生强制类型转换会报错。(class cast exception类型转换异常)
instanceof关键字使用
animal1是一个指向animal对象的引用
animal2是一个指向dog类对象的引用
转型关键字可以搭配分支语句使用图中类似地址的一串字符是因为没有编辑toString方法)
选择题常考点:
只要一个类中有抽象方法,就必须是抽象类
关于抽象类易混淆点:
只要这个类是abstruct抽象类,没法具体到给自己实例化对象:
idea中抽象类和普通类图标有区别,下图示例。Shape shape = new Shape();
// 编译出错
Error:(30, 23) java: Shape是抽象的; 无法实例化
如果子类也是抽象类可以不用覆写:比如此时的B类可以选择性覆写父类A的printA方法。此时普通C类就必须覆写两个A类和B类中的方法,但是如果B类中覆写了A类printA方法,此时C类就只用覆写B类中printB方法,因为A类的方法被B类覆写过了。
抽象类只是比普通类多了一些抽象方法:
new的对象是fun,但是它继承了父类,抽象类以遵循继承规则,所以先产生父类baseTest,调用父类构造方法,父类中print方法被子类覆写,所以触发子类中的覆写方法print。(先调用父类构造方法,在调用子类构造方法)
抽象方法不能是 private 的abstract class Shape {
abstract private void draw();
}
// 编译出错
Error:(4, 27) java: 非法的修饰符组合: abstract和private
abstract class Shape {
abstract public void draw();
void func() {
System.out.println("func");
}
}
class Rect extends Shape {
...
}
public class Test {
public static void main(String[] args) {
Shape shape = new Rect();
shape.func();
}
}
// 执行结果
func
抽象类本身不能被实例化, 要想使用, 只能创建该抽象类的子类. 然后让子类重写抽象类中的抽象方法.
确实如此. 但是使用抽象类相当于多了一重编译器的校验.
很多语法存在的意义都是为了 “预防出错”, 例如我们曾经用过的 final 也是类似. 创建的变量用户不去修改, 不就相当于常量嘛? 但是加上 final 能够在不小心误修改的时候, 让编译器及时提醒我们.充分利用编译器的校验, 在实际开发中是非常有意义的.
interface IShape {
void draw();
}
class Cycle implements IShape {
@Override
public void draw() {
System.out.println("○");
}
}
public class Test {
public static void main(String[] args) {
IShape shape = new Rect();
shape.draw();
}
}
然而 Java 中只支持单继承, 一个类只能 extends 一个父类. 但是可以同时实现多个接口, 也能达到多继承类似的效果
子类实现接口,而接口自身不能被自身实例化
computer类相当于接口载体
如何实现接口
假设新增一个满足接口的类
接口里可以省略的关键字:
接口和抽象类一样都不能自己实例化对象,只能靠子类发生向上转型。但是不同点是接口范围更广,由下图实例可以看出这些子类都没有关系。
想要输出当前类属性就得覆写toString方法
println默认都是调用该类的toString方法
equals方法一般默认比较的是地址,所以要比较属性值需要我们自己覆写,这是每一个编程人员必须掌握的方法
两个对象属性值的比较需要覆写equal方法(重点,必须掌握)
注意观察每行代码的注释。
2.首先判断两个对象是不是同一个类比较,如果不是一个类不可比较,直接返回false
3.name属性使用equals方法是因为name是String引用类型有自带的比较equals方法,所有引用类型比较都必须使用equals方法。JDK所有引用类型都自带equals方法不需要你覆写
为什么要使用向下转型?此时object是要比较的类的父类引用,只是用来接收比较对象的,之前提到过,要用子类的独有的属性和方法时,必须向下转型。
此时除了8大基本类型没法用Object类来接收以外,所有类型都能使用Object来接收。
下图中接口引用:接口之间发生向上转型
自定义类型比较用array.sort比较是分辨不出来谁大谁小的
person类和整型不一样,大小无法像整形一样一目了然。
内部是如何比较的,比较的不一定是数值上的大小。
return this.age-per.age如果大于0就会走冒泡排序的循环,默认升序
想要实现降序可以:
此时分支中小于0就不会走分支,一般Arrays.sort默认是升序排列
JVM在运行时会检查所有实现了
cloneable接口的子类,赋予其克隆的能力。
clone方法是Object提供的方法。
克隆方法内部定义:
实现:animal2是通过animal1克隆而来的,确实是两个独立对象,但是属性值相同。
第一行打印地址
第二行打印内部属性
此时animal3不叫克隆
克隆对象不会使用该类的构造方法
b1.a和b2.a都指向同一个对象A
如之前写的 Animal 例子. 此处的 Animal 中包含一个 name 这样的属性, 这个属性在任何子类中都是存在的. 因此此处的 Animal 只能作为一个抽象类, 而不应该成为一个接口
抽象类存在的意义是为了让编译器更好的校验, 像 Animal 这样的类我们并不会直接使用, 而是使用它的子类. 万
一不小心创建了 Animal 的实例, 编译器会及时提醒我们



