Java 中对于字段和方法共有四种访问权限:
-
private: 类内部能访问, 类外部不能访问.
-
默认(也叫包访问权限): 类内部能访问, 同一个包中的类可以访问, 其他类不能访问.
-
protected: 类内部能访问, 子类和同一个包中的类可以访问, 其他类不能访问.
-
public : 类内部和类的调用者都能访问.
final 关键字
修饰一个变量或者字段的时候, 表示 常量 (不能修改).final 关键字也能修饰类, 此时表示被修饰的类就不能被继承(即该类不会有子类).
final public class Animal {
}
public class Bird extends Animal {
}
// 编译出错
Error:java: 无法从最终Animal进行继承
多态
==
向上转型
举个栗子:
Bird bird = new Bird("圆圆");
这行代码也可以这样写:
Bird bird = new Bird("圆圆");
Animal bird2 = bird;
// 或者写成下面的方式
Animal bird2 = new Bird("圆圆");
此时 bird2 是一个父类 (Animal) 的引用, 指向一个子类 (Bird) 的实例. 这种写法称为 向上转型.
语法:
父类名称 父类引用 = new 子类()
向上转型发生的时机:
-
直接赋值
-
方法传参
-
方法返回
方法重写
子类实现父类的同名方法, 并且参数的类型和个数完全相同, 这种情况称为 覆写/重写/覆盖(Override).
注意事项:
-
子类中覆写的权限 >= 父类的权限.
-
子类的方法返回值必须与父类保持一致(向上转型除外).
-
普通方法可以重写, static 修饰的静态方法不能重写.
-
针对重写的方法, 可以使用 @Override 注解来显式指定.
动态绑定
当子类和父类中出现同名方法的时候, 再去调用会出现什么情况呢?
// Animal.java
public class Animal {
protected String name;
public Animal(String name) {
this.name = name;
}
public void eat(String food) {
System.out.println("我是一只小动物");
System.out.println(this.name + "正在吃" + food);
}
}
// Bird.java
public class Bird extends Animal {
public Bird(String name) {
super(name);
}
public void eat(String food) {
System.out.println("我是一只小鸟");
System.out.println(this.name + "正在吃" + food);
}
}
// Test.java
public class Test {
public static void main(String[] args) {
Animal animal1 = new Animal("圆圆");
animal1.eat("谷子");
Animal animal2 = new Bird("扁扁");
animal2.eat("谷子");
}
}
// 执行结果
我是一只小动物
圆圆正在吃谷子
我是一只小鸟
扁扁正在吃谷子
此时, 我们发现:
animal1 和 animal2 虽然都是Animal类型的实例, 但是 animal1 指向Animal类型的实例, animal2 指向Bird类型的实例。
针对 animal1 和 animal2 分别调用 eat 方法, 发现animal1.eat()实际调用了父类的方法, 而animal2.eat()实际调用了子类的方法。
因此,只看new的是谁,就调用的是谁的方法。
理解多态
多态:当一个引用在不同场景下调用相同方法表现出不同行为。
举个栗子:
class Shape {
public void draw() {
// 啥都不用干
}
}
class Cycle extends Shape {
@Override
public void draw() {
System.out.println("○");
}
}
class Rect extends Shape {
@Override
public void draw() {
System.out.println("□");
}
}
class Flower extends Shape {
@Override
public void draw() {
System.out.println("♣");
}
}
/我是分割线//
// Test.java
public class Test {
public static void main(String[] args) {
Shape shape1 = new Flower();
Shape shape2 = new Cycle();
Shape shape3 = new Rect();
drawMap(shape1);
drawMap(shape2);
drawMap(shape3);
}
// 打印单个图形
public static void drawShape(Shape shape) {
shape.draw();
}
}
当类的调用者在编写 drawMap 这个方法的时候, 参数类型为 Shape (父类), 此时在该方法内部并不知道, 也不关注当前的 shape 引用指向的是哪个类型(哪个子类)的实例. 此时 shape 这个引用调用 draw 方法可能会有多种不同的表现(和 shape 对应的实例相关), 这种行为就称为 多态。
多态顾名思义, 就是 “一个引用, 能表现出多种不同形态”,就是方法重写 + 向上转型,
使用多态的好处:
-
类调用者对类的使用成本进一步降低.
-
能够降低代码的 “圈复杂度”, 避免使用大量的 if - else.
-
可扩展能力更强(当有一个新子类产生时,由于多态性,调用者只需创建一个新类的实例就可以了).
向下转型
向上转型是子类对象转成父类对象, 向下转型就是父类对象转成子类对象.
语法:
子类 子类引用 = (子类)父类引用
注意事项:
向下转型可能会出错,两种方法避免出错:
-
使用Java提供的instanceof关键字(instanceof 可以判定一个引用是否是某个类的实例. 如果是, 则返回 true. 这时再进行向下转型就比较安全)
-
要使用向下转型,先得发生向上转型(核心就在于使用向下转型的对象恰好是当前向上转型new出来的)
例如:Animal a1 = new Bird();
Bird bird = (Bird) a1;
抽象类
===
语法规则:
在刚才的打印图形例子中, 我们发现, 父类 Shape 中的 draw 方法好像并没有什么实际工作, 主要的绘制图形都是由Shape 的各种子类的 draw 方法来完成的. 像这种没有实际工作的方法, 我们可以把它设计成一个 抽象方法(abstractmethod), 包含抽象方法的类我们称为 抽象类(abstract class).
abstract class Shape {
abstract public void draw();
}
-
在 draw 方法前加上 abstract 关键字, 表示这是一个抽象方法. 同时抽象方法没有方法体(没有 { }, 不能执行具体代码).
-
对于包含抽象方法的类, 必须加上 abstract 关键字表示这是一个抽象类.
注意事项:
-
抽象类使用abstract定义,并且抽象类是普通类的超集(普通类有的东西,抽象类都有).
-
抽象类只是比普通类多了一个方法而已.
-
抽象方法使用abstract定义,只有方法的声明,没有方法体.
抽象类的作用
抽象类存在的最大意义就是为了被继承.
抽象类本身不能被实例化, 要想使用, 只能创建该抽象类的子类. 然后让子类重写抽象类中的抽象方法.
接口
===
接口是抽象类的更进一步. 抽象类中还可以包含非抽象方法, 和字段. 而接口中包含的方法都是抽象方法, 字段只能包含静态常量.
语法规则
-
使用 interface 定义一个接口.
-
接口中的方法一定是抽象方法, 因此可以省略 abstract.
-
接口中的方法一定是 public, 因此可以省略 public.
-
使用 implements 继承接口. 此时表达的含义不再是 “扩展”, 而是 “实现”.
-
在调用的时候同样可以创建一个接口的引用, 对应到一个子类的实例.
-
接口不能单独被实例化.
注意事项:
-
我们创建接口的时候, 接口的命名一般以大写字母 I 开头.
-
接口的命名一般使用 “形容词” 词性的单词.
-
阿里编码规范中约定, 接口中的方法和属性不要加任何修饰符号, 保持代码的简洁性.
-
子类命名:接口名称 + impl.
-
多个接口之间可以用extends继承父接口.
-
一个类如果同时继承抽象类,实现接口,先extends一个类,然后implements多个接口.
-
JDK8之后,接口中也允许出现普通方法.
Comparable和Clonable 接口
Java 中内置了一些很有用的接口, 例如Comparable和Clonable 。
Comparable
Comparable 接口中的CompareTo方法:返回值int
-
如果当前对象应排在参数对象之前, 返回小于 0 的数字;
-
如果当前对象应排在参数对象之后, 返回大于 0 的数字;
-
如果当前对象和参数对象不分先后, 返回 0;
Clonable
表示克隆的能力。
浅拷贝:
当一个对象是通过另一个对象clone出来的.此时这两个对象是独立的两个对象,但是这两个对象的内部包含的其他一弄是相同的,这种拷贝成为浅拷贝。
Cloneable 拷贝出的对象是一份 “浅拷贝”
看以下代码:
public class Test {
static class A implements Cloneable {
public int num = 0;
@Override
public A clone() throws CloneNotSupportedException {
return (A)super.clone();
# 总结
上述知识点,囊括了目前互联网企业的主流应用技术以及能让你成为“香饽饽”的高级架构知识,每个笔记里面几乎都带有实战内容。
**很多人担心学了容易忘,这里教你一个方法,那就是重复学习。**
打个比方,假如你正在学习 spring 注解,突然发现了一个注解@Aspect,不知道干什么用的,你可能会去查看源码或者通过博客学习,花了半小时终于弄懂了,下次又看到@Aspect 了,你有点郁闷了,上次好像在哪哪哪学习,你快速打开网页花了五分钟又学会了。
从半小时和五分钟的对比中可以发现多学一次就离真正掌握知识又近了一步。
![](https://img-blog.csdnimg.cn/img_convert/083705900033c18eedb981508f638f0c.webp?x-oss-process=image/format,png)
人的本性就是容易遗忘,只有不断加深印象、重复学习才能真正掌握,所以很多书我都是推荐大家多看几遍。哪有那么多天才,他只是比你多看了几遍书。
ic class A implements Cloneable {
public int num = 0;
@Override
public A clone() throws CloneNotSupportedException {
return (A)super.clone();
# 总结
上述知识点,囊括了目前互联网企业的主流应用技术以及能让你成为“香饽饽”的高级架构知识,每个笔记里面几乎都带有实战内容。
**很多人担心学了容易忘,这里教你一个方法,那就是重复学习。**
打个比方,假如你正在学习 spring 注解,突然发现了一个注解@Aspect,不知道干什么用的,你可能会去查看源码或者通过博客学习,花了半小时终于弄懂了,下次又看到@Aspect 了,你有点郁闷了,上次好像在哪哪哪学习,你快速打开网页花了五分钟又学会了。
从半小时和五分钟的对比中可以发现多学一次就离真正掌握知识又近了一步。
[外链图片转存中...(img-PmFC0Thj-1719275762667)]
人的本性就是容易遗忘,只有不断加深印象、重复学习才能真正掌握,所以很多书我都是推荐大家多看几遍。哪有那么多天才,他只是比你多看了几遍书。