System.out.println("Person constructor");
}
public class Student extends Person {
static {
System.out.println("Student static block");
}
{
System.out.println("Student block");
}
public Student() {
System.out.println("Student constructor");
}
}
```
**执行顺序**:父类静态块 -> 子类静态块 -> 父类代码块 -> 父类构造器 -> 子类代码块 -> 子类构造器
```
public static void main(String[] args) {
new Student();
// Person static block
// Student static block
// Person block
// Person constructor
// Student block
// Student constructor
}
```
[](https://gitee.com/vip204888/java-p7)单例模式(Singleton Pattern)
==========================================================================================
如果一个类设计成单例模式,那么在程序运行过程中,这个类只能创建一个实例。
**饿汉式单例模式**:像饿汉一样,上来就直接创建了唯一的那个实例。(线程安全)
```
/*
* 饿汉式单例模式
*/
public class Rocket {
private static Rocket instance = new Rocket();
private Rocket(){}
public static Rocket getInstance() {
return instance;
}
}
```
**懒汉式单例模式**:像懒汉一样,只有用到的时候采取创建实例。(线程不安全)
```
/*
* 懒汉式单例模式
*/
public class Rocket {
private static Rocket instance = null;
private Rocket(){}
public static Rocket getInstance() {
if (instance == null) {
instance = new Rocket();
}
return instance;
}
}
```
[](https://gitee.com/vip204888/java-p7)final、常量(Constant)
=====================================================================================
被 `final` 修饰的类:**不能被子类化,不能被继承**
被 `final` 修饰的方法:**不能被重写**
被 `final` 修饰的变量:**只能进行1次赋值**
常量的写法:
* `public static final double PI = 3.14159265358979323846;`
* `private static final int NOT_FOUND = - 1;`
如果将**基本类型**或**字符串定义为常量**,并且**在编译时就能确定值**:
* **编译器会使用常量值替代各处的常量名**(类似于 C 语言的宏替换)
* 称为**编译时常量**( compile-time constant)
例如下面的情况,**编译时会被替换**:
```
public class Main {
static final int A = 123456;
static final String B = "HELLO";
public static void main(String[] args) {
System.out.println(A); // 编译时直接被替换为下面
// System.out.println(123456);
System.out.println(B); // 编译时直接被替换为下面
// System.out.println("HELLO");
}
}
```
下面这种情况,**编译时无法确定值,不会被替换**:
```
public class Main {
static int NUMBER = getNum();
static int getNum() {
int a = 10;
int b = 20;
return a + b * 2 + 6;
}
public static void main(String[] args) {
System.out.println(NUMBER); // 不会被替换
}
}
```
[](https://gitee.com/vip204888/java-p7)嵌套类(Nested Class)
====================================================================================
* **嵌套类**:定义在另一个类中的类;
* 在嵌套类外层的类,称为:**外部类**(Outer Class)
* 最外层的外部类,称为:**顶级类**(Top-level Class)
```
public class OuterClass { // 顶级类
// 静态嵌套类
static class StaticNestedClass {
}
// 非静态嵌套类(内部类)
class InnerClass {
}
}
```
[](https://gitee.com/vip204888/java-p7)内部类(Inner Class)
-----------------------------------------------------------------------------------
**内部类**:没有被 `static` 修饰的嵌套类,**非静态嵌套类**
跟实例变量、实例方法一样,内部类与外部类的实例相关联:
* 必须先创建外部类实例,然后再用外部类实例创建内部类实例
* 内部类不能定义除**编译时常量**以外的任何 static 成员
**内部类与外部类**:
* 内部类可以直接访问外部类中的所有成员(即使是 `private`)
* 外部类可以直接访问内部类实例的成员变量、方法(即使是 `private`)
内部类示例:先有公司 `company` 才能有员工 `employee`,将 `employee` 设置为 `company` 的内部类,而 `company` 可以访问 `employee` 的实例的成员变量、方法(包括`private`),`employee` 可以访问 `company` 的所有成员(包括 `private`)。
```
public class Company {
private String name;
public Company(String name) {
this.name = name;
}
public void fire(Employee e) {
// 外部类可以直接访问内部类实例的成员变量(包括 private)
System.out.println(name + " fire " + e.no);
}
public class Employee {
private int no;
public Employee(int no) {
this.no = no;
}
public void show() {
// 内部类可以直接访问外部类中的所有成员(包括 private)
System.out.println(name + " : " + no);
}
}
public static void main(String[] args) {
Company c = new Company("Google");
Employee e = c.new Employee(17210224);
e.show();
c.fire(e);
}
}
```
**内部类的细节**:如果有**同名变量**,默认访问内部的,访问外部的需要特别指出。
```
public class OuterClass {
private int x = 1; // 外部类变量x
public class InnerClass {
private int x = 2; // 内部类变量x
public void show() {
// 默认访问内部
System.out.println(x); // 2
System.out.println(this.x); // 2
// 访问外部类的同名变量需要这么写
System.out.println(OuterClass.this.x); // 1
}
}
public static void main(String[] args) {
new OuterClass().new InnerClass().show();
}
}
```
### [](https://gitee.com/vip204888/java-p7)内部类内存分布
```
public class Person {
private int age;
public class Hand {
private int weight;
}
public static void main(String[] args) {
// 必须先有Person对象才能创建Hand对象
Person p1 = new Person();
Hand h1 = p1.new Hand();
Person p2 = new Person();
Hand h2 = p2.new Hand();
}
}
```
在 `Hand` 类被释放前,`Person` 类不会被释放(被 `Hand` 指向着)
![在这里插入图片描述](https://img-blog.csdnimg.cn/20200425081915382.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80MzczNDA5NQ==,size_16,color_FFFFFF,t_70)
[](https://gitee.com/vip204888/java-p7)静态嵌套类(Static Nested Class)
---------------------------------------------------------------------------------------------
**静态嵌套类**:被 `static` 修饰的**嵌套类**;
静态嵌套类在**行为上**就是一个顶级类,只是定义的代码写在了另一个类中;
对比一般的顶级类,静态嵌套类多了一些特殊权限
* 可以直接访问外部类中的成员(即使被声明为 `private`)
```
public class Person {
private int age;
private static int count = 1;
private static void run() {
System.out.println("Person - run");
}
public static class Car { // 静态嵌套类
public void test() {
Person person = new Person();
// 静态嵌套类可以直接访问外部类中的成员(包括 private)
System.out.println(person.age); // 0
Person.count = 1;
Person.run(); // Person - run
System.out.println(count); // 1
run(); // Person - run
}
}
}
public static void main(String[] args) {
Person p = new Person();
// 静态嵌套类的使用
Person.Car c = new Person.Car();
// 如果之前 import Person.Car; 可以直接使用;
// Car c = new Car();
c.test();
}
[](https://gitee.com/vip204888/java-p7)什么情况使用嵌套类?
-----------------------------------------------------------------------------
如果类 A 只用在类 C 内部,可以考虑将类 A 嵌套到类 C 中;
* 封装性更好
* 程序包更加简化
* 增强可读性、维护性
如果类 A 需要经常访问类 C 的**非公共成员**,可以考虑将类 A嵌套到类 C 中;
* 另外也可以根据需要将类 A 隐藏起来,不对外暴露
如果需要**经常访问非公共的实例成员**,设计成内部类(非静态嵌套类),否则设计成静态嵌套类;
* 如果必须先有 C 实例,才能创建 A 实例,那么可以将 A 设计为 C 的内部类
[](https://gitee.com/vip204888/java-p7)局部类(Local Class)
===================================================================================
**局部类**:定义在**代码块**中的类(可以定义在方法中、for 循环中、if 语句中等)
局部类不能定义除**编译时常量**以外的任何 `static` 成员;
局部类只能访问 `final` 或者 **有效`final`** 的局部变量;
* 从 Java 8 开始,如果**局部变量没有被第二次赋值**,就认定为是**有效`final`**
局部类可以直接访问外部类中的所有成员(即使被声明为 `private`)
* 局部类只有定义在实例相关的代码块中,才能直接访问外部类中的实例成员(实例变量、实例方法)
局部类示例:
public class TestLocalClass {
private int a = 1;
private static int b = 2;
private static void test1() {}
private void test2() {}
public void test3() {
int c = 2;
class LocalClass {
static final int d = 4;
void test4() {
System.out.println(a + b + c + d);
test1();
test2();
}
}
new LocalClass().test4();
}
}
[](https://gitee.com/vip204888/java-p7)抽象类(Abstract Class)与接口(Interface)
====================================================================================================
[](https://gitee.com/vip204888/java-p7)抽象类
----------------------------------------------------------------------
**抽象方法**:被 `abstract` 修饰的实例方法
* 只有方法声明,没有方法实现(参数列表后面没有大括号,而是分号)
* 不能是 `private` 权限(因为定义抽象方法的目的让子类去实现)
* 只能定义在抽象类、接口中
**抽象类**:被 `abstract` 修饰的类
* 可以定义抽象方法
* 不能实例化,但可以自定义构造方法
* 子类必须实现抽象父类中的所有抽象方法(除非子类也是一个抽象类)
* 可以像非抽象类一样定义成员变量、常量、嵌套类型、初始化块、非抽象方法等
也就说,**抽象类也可以完全不定义抽象方法**
常见使用场景:
* 抽取子类的公共实现到抽象父类中,要求子类必须要单独实现的定义成抽象方法
实例:
public abstract class Shape {
protected double area;
protected double girth;
public double getArea() {
return area;
}
public double getGirth() {
return girth;
}
public void show() {
calculate();
System.out.println(area + "_" + girth);
}
protected abstract void calculate();
}
public class Rectangle extends Shape {
private double width;
private double height;
public Rectangle(double width, double height) {
super();
this.width = width;
this.height = height;
}
@Override
protected void calculate() {
area = width * height;
girth = (width + height) * 2;
}
}
public class Circle extends Shape {
private double radius;
public Circle(double radius) {
this.radius = radius;
}
@Override
protected void calculate() {
double half = Math.PI * radius;
area = half * radius;
girth = half * 2;
}
}
public static void main(String[] args) {
Rectangle rectangle = new Rectangle(10, 20);
rectangle.show();
Circle circle = new Circle(30);
circle.show();
}
```
[](https://gitee.com/vip204888/java-p7)接口(Interface)
--------------------------------------------------------------------------------
**API**(Application Programming Interface)
* **应用编程接口**,提供给开发者调用的一组功能(无须提供源码)
**Java 中的接口**:
* 一系列方法声明的集合
* 用来定义规范、标准
**接口中可以定义的内容**:
* **抽象方法(可以省略 abstract)**
* **常量(可以省略 static、final)**
* **嵌套类型**
* 从 Java 8 开始可以定义:**默认方法(`default`)**、**静态方法**
上述可以定义的内容都是隐式 public 的,因此可以省略 public 关键字
* 从 Java 9 开始可以定义:**private 方法**
* 不能自定义构造方法、不能定义(静态)初始化块、不能实例化
**接口的细节**:
一个类可以通过 `implements` 关键字实现一个或多个接口
* 实现接口的类必须实现接口中定义的所有抽象方法,除非它是个抽象类
* 如果一个类实现的多个接口中有相同的抽象方法,只需要实现此方法一次
* `extends` 和 i`mplements` 可以一起使用,`implements` 必须写在 `extends` 的后面
* 当父类、接口中的方法签名一样时,那么返回值类型也必须一样
一个接口可以通过 `extends` 关键字继承一个或者多个接口
* 当多个父接口中的方法签名一样时,那么返回值类型也必须一样
[](https://gitee.com/vip204888/java-p7)抽象与接口的对比(如何选择)
---------------------------------------------------------------------------------
抽象类和接口的用途还是有点类似,该如何选择?
何时选择**抽象类**?
* 在**紧密相关的类**之间共享代码
* 需要除 public 之外的访问权限
* 需要定义实例变量、非 final 的静态变量
何时选择**接口**?
* **不相关的类**实现相同的方法
* 只是**定义行为**,不关心具体是谁实现了行为
* 想实现类型的**多重继承**
[](https://gitee.com/vip204888/java-p7)接口的升级问题(默认方法、静态方法)
=====================================================================================
如果接口需要升级,比如增加新的抽象方法:会导致大幅的代码改动,以前实现接口的类都得改动
若想在不改动以前实现类的前提下进行接口升级,从 Java 8 开始,有 2 种方案:
* **默认方法**(Default Method)
* **静态方法**(Static Method)
[](https://gitee.com/vip204888/java-p7)默认方法(Default Method)
---------------------------------------------------------------------------------------
* 用 `default` 修饰默认方法
* 默认方法只能是实例方法
**默认方法的使用**:
当一个类实现的接口中有默认方法时,这个类可以:
* **啥也不干**,沿用接口的默认实现
* 重新定义默认方法,**覆盖**默认方法的实现
* 重新声明默认方法,将默认方法声明为抽象方法(此类必须是抽象类)
当一个接口继承的父接口中有默认方法时,这个接口可以:
* **啥也不干**,沿用接口的默认实现
* 重新定义默认方法,**覆盖**默认方法的实现
* 重新声明默认方法,将默认方法声明为抽象方法
简单示例:`Eatable` 中有默认方法,`Dog`啥也不干,`Cat` 覆盖默认方法。
```
public interface Eatable {
// 默认方法
default void eat(String name) {
System.out.println("Eatable - eat - " + name);
}
}
// Eatable接口中新增了方法, 但是没有影响到Dog类
public class Dog implements Eatable {}
// Eatable接口中新增了方法, Cat类中可以覆盖
public class Cat implements Eatable {
@Override
public void eat(String name) {
Eatable.super.eat(name);
System.out.println("Cat - eat - " + name);
}
}
public static void main(String[] args) {
Dog dog = new Dog();
dog.eat("bone");
// Eatable - eat - bone
Cat cat = new Cat();
cat.eat("fish");
// Eatable - eat - fish
// Cat - eat - fish
}
如果父类定义的非抽象方法与接口的默认方法相同时,最终将调用父类的方法(**就近原则**):
public class Animal {
public void run() {
System.out.println("Animal - run");
}
}
public interface Runnable {
default void run() {
System.out.println("Runnable - run");
}
}
public class Dog extends Animal implements Runnable {}
public static void main(String[] args) {
Dog dog = new Dog();
dog.run(); // 继承的父类、实现的接口中都有 run() 方法, 默认调用父类的
// Animal - run
}
```
如果父类定义的抽象方法与接口的默认方法相同时,要求子类实现此抽象方法
* 可以通过 `super` 关键字调用接口的默认方法
```
public interface Runnable {
default void run() {
System.out.println("Runnable - run");
}
}
public abstract class Animal {
public void run() {}
}
public class Dog extends Animal implements Runnable {
@Override
public void run() { // 父类的抽象方法run方法与接口中的run方法相同, 要求实现父类的抽象方法
Runnable.super.run(); // 可以通过super调用接口的默认方法
System.out.println("Dog - run");
// Runnable - run
// Dog - run
}
}
```
如果(父)接口定义的默认方法与其他(父)接口定义的方法相同时,要求子类型实现此默认方法:
例:`Runnable` 和 `Walkable` 两个父接口中定义的**默认方法**都是 `run()`,`Testable` 继承了两个父类,则要求实现默认方法 `run()` ,`Dog` 类同理。
```
public interface Runnable {
default void run() {
System.out.println("Runnable - run");
}
}
public interface Walkable {
default void run() {
System.out.println("Walkable - run");
}
}
// Testable 父接口继承了 Runnable 父接口和 Walkable 父接口
// 他们都有默认方法 run, 要求 Testable 接口实现该默认方法
public interface Testable extends Runnable, Walkable {
@Override
default void run() {
Runnable.super.run();
Walkable.super.run();
System.out.println("Testable - run");
}
}
// Dog 类实现了 Runnable、Walkable 两个接口
// 他们都有默认方法 run, 要求 Dog 类实现该默认方法
public class Dog implements Runnable, Walkable {
@Override
public void run() {
Runnable.super.run();
Walkable.super.run();
System.out.println("Dog - run");
}
}
public static void main(String[] args) {
Dog dog = new Dog();
dog.run();
// Runnable - run
// Walkable - run
// Dog - run
}
```
再看一个例子:
```
public interface Animal {
default String myself() {
return "I am an animal.";
}
}
public interface Fire extends Animal {}
public interface Fly extends Animal {
@Override
default String myself() {
return "I am able to fly.";
}
}
public class Dragon implements Fly, Fire {}
public static void main(String[] args) {
Dragon dragon = new Dragon();
System.out.println(dragon.myself());
// I am able to fly.
}
```
[](https://gitee.com/vip204888/java-p7)静态方法(Static Method)
--------------------------------------------------------------------------------------
* 接口中定义的静态方法只能通过接口名调用,**不能被继承**;
```
public interface Eatable {
static void eat(String name) {
System.out.println("Eatable - eat - " + name);
}
}
public interface Sleepable {
static void eat(String name) {
System.out.println("Sleepable - eat - " + name);
}
}
public interface Dog extends Sleepable, Eatable {
static void eat(String name) {
System.out.println("Dog - eat - " + name);
}
}
public static void main(String[] args) {
Dog.eat("1");
Eatable.eat("1");
Sleepable.eat("3");
// Dog - eat - 1
// Eatable - eat - 1
// Sleepable - eat - 3
}
[](https://gitee.com/vip204888/java-p7)多态(Polymorphism)
===================================================================================
什么是多态?
* 具有多种形态
* 同一操作作用于不同的对象,产生不同的执行结果
多态的体现:
* **父类(接口)类型指向子类对象**
* **调用子类重写的方法**
JVM 会根据引用变量指向的具体对象来调用对应的方法:
* 这个行为叫做:虚方法调用(virtual method invocation)
* 类似于 C++ 中的虚函数调用
多态示例:
public class Animal {
public void speak() {
System.out.println("Animal - speak");
}
}
public class Dog extends Animal {
@Override
public void speak() {
System.out.println("Dog - wangwang");
}
}
public class Cat extends Animal {
@Override
public void speak() {
System.out.println("Cat - miaomiao");
}
}
public static void main(String[] args) {
speak(new Dog()); // Dog - wangwang
speak(new Cat()); // Cat - miaomiao
}
// 多态的体现: 父类(接口)类型指向子类对象, 调用子类重写的方法
static void speak(Animal animal) {
animal.speak();
}
```
# 总结
谈到面试,其实说白了就是刷题刷题刷题,天天作死的刷。。。。。
为了准备这个“金三银四”的春招,狂刷一个月的题,狂补超多的漏洞知识,像这次美团面试问的算法、数据库、Redis、设计模式等这些题目都是我刷到过的
**并且我也将自己刷的题全部整理成了PDF或者Word文档(含详细答案解析),[有需要的朋友可以戳这里即可免费领取](https://gitee.com/vip204888/java-p7)**
![我的美团offer凉凉了?开发工程师(Java岗)三面结束等通知...](https://img-blog.csdnimg.cn/img_convert/770d2ba0dacfa221245720f94143747b.png)
66个Java面试知识点
**架构专题(MySQL,Java,Redis,线程,并发,设计模式,Nginx,Linux,框架,微服务等)+大厂面试题详解(百度,阿里,腾讯,华为,迅雷,网易,中兴,北京中软等)**
![我的美团offer凉凉了?开发工程师(Java岗)三面结束等通知...](https://img-blog.csdnimg.cn/img_convert/4b2512d8637836fb6581d4c9134d0acc.png)
**算法刷题(PDF)**
```
[](https://gitee.com/vip204888/java-p7)多态(Polymorphism)
===================================================================================
什么是多态?
* 具有多种形态
* 同一操作作用于不同的对象,产生不同的执行结果
多态的体现:
* **父类(接口)类型指向子类对象**
* **调用子类重写的方法**
JVM 会根据引用变量指向的具体对象来调用对应的方法:
* 这个行为叫做:虚方法调用(virtual method invocation)
* 类似于 C++ 中的虚函数调用
多态示例:
```
public class Animal {
public void speak() {
System.out.println("Animal - speak");
}
}
public class Dog extends Animal {
@Override
public void speak() {
System.out.println("Dog - wangwang");
}
}
public class Cat extends Animal {
@Override
public void speak() {
System.out.println("Cat - miaomiao");
}
}
public static void main(String[] args) {
speak(new Dog()); // Dog - wangwang
speak(new Cat()); // Cat - miaomiao
}
// 多态的体现: 父类(接口)类型指向子类对象, 调用子类重写的方法
static void speak(Animal animal) {
animal.speak();
}
# 总结
谈到面试,其实说白了就是刷题刷题刷题,天天作死的刷。。。。。
为了准备这个“金三银四”的春招,狂刷一个月的题,狂补超多的漏洞知识,像这次美团面试问的算法、数据库、Redis、设计模式等这些题目都是我刷到过的
**并且我也将自己刷的题全部整理成了PDF或者Word文档(含详细答案解析),[有需要的朋友可以戳这里即可免费领取](https://gitee.com/vip204888/java-p7)**
[外链图片转存中...(img-QSnG07b9-1628506140106)]
66个Java面试知识点
**架构专题(MySQL,Java,Redis,线程,并发,设计模式,Nginx,Linux,框架,微服务等)+大厂面试题详解(百度,阿里,腾讯,华为,迅雷,网易,中兴,北京中软等)**
[外链图片转存中...(img-ceKJaSuI-1628506140108)]
**算法刷题(PDF)**
![我的美团offer凉凉了?开发工程师(Java岗)三面结束等通知...](https://img-blog.csdnimg.cn/img_convert/2ba1bf4a59eb3579867ebfb6dafbdc0c.png)