1、面向过程、面向对象的区别
面向过程:考虑怎么做(具体在于过程)。
面向对象:考虑谁来做(具体在于谁,即在于对象)。
2、类和对象的关系
类:主要用于从对象抽象出一些公共特征,形成一个类。
对象:万事万物皆对象,可以是具体的事物、实体、实例。
3、面向对象三个阶段
OOA(Object Oriented Analysis):面向对象分析
比如猫、 狗、仓鼠等 ====> 抽象出一个类:宠物
比如宠物 、昆虫等 ====> 抽象出一个类:动物
OOD(Object Oriented Design):面向对象设计
比如宠物有什么行为和特征
- 行为:吃饭、睡觉等 ===> eat、sleep等方法
- 特征:品种、肤色等 ===> kind、color等属性
OOP(Object Oriented Programming):面向对象编程
比如具体的eat方法 ===> public void eat{System.out.print(“正在进食,请勿打扰!!!”)}
4、创建类
类包括:属性、方法、构造器、代码块、内部类。
5、创建对象的生命周期
创建对象过程:
- 对类加载,只加载一次。
- 在堆中开辟空间。
- 对对象初始化操作,属性默认初始化赋值。
- new关键字调用构造器重新赋值。
6、局部变量和成员变量
6.1、局部变量和成员变量区别
区别 | 局部变量 | 成员变量 |
---|---|---|
位置不同 | 方法中 | 类中方法外 |
内存位置不同 | 栈内存 | 堆内存 |
作用范围不同 | 当前方法中(当前代码块中) | 当前类的很多方法 |
作用时间不同 | 方法执行到执行完毕 | 对象创建到销毁 |
是否有默认值 | 无 | 有初始化值 |
是否要初始化 | 需要,否则在操作会报错 | 无需初始化 |
6.2、基本类型初始化默认值
引用类型默认值:null
基本数据类型 | 默认值 |
---|---|
byte | (byte)0 |
short | (short)0 |
int | 0 |
long | 0L |
float | 0.0f |
double | 0.0d |
boolean | false |
char | ‘/u0000’(null) |
7、构造器
构造器的特点:
- 无返回值
- 没有返回类型
- 构造器名字应与类名一致
- 不可被final、static、abstract等修饰
构造器的作用:给属性赋值。(并不是创建对象)
8、this
this代表当前对象
this修饰变量:如果局部变量或者参数与成员变量名称相同,用this修饰代表使用的是成员变量,避免就近原则。
this修饰方法:同一个类方法互相调用,this可省略不写
this修饰构造器:同一个类中的构造器可用this互相调用
this修饰构造器必须放在第一行
原因:为了避免多次调用构造器给对象初始化赋值,同一方法内只能调用一次this()或super() ===> 由于构造器中第一行如果没有this()或super(),默认会有一个super()。如果this和super都存在就会多次调用父类构造器
9、static
修饰:属性、方法、代码块、内部类。
9.1、修饰属性
多线程共享变量可用static修饰
- 在类加载时加载到方法区的静态域中
- 先于对象存在
- 调用方式:类.属性名、对象.属性名
9.2、修饰方法
- 在类加载时加载到方法区的静态域中
- 先于对象存在
- 静态方法中不能访问非静态属性和方法
- 静态方法中不能用this(由于先于对象存在)
- 调用方式:类.方法名、对象.方法名
9.3、修饰代码块
static修饰代码块,叫静态代码块,最先执行。
9.4、修饰内部类
static修饰的内部类,可以作为普通类来使用。
10、代码块
代码块分类:普通块,构造块,静态块,同步块(多线程)
代码块执行顺序:
- 最先执行静态代码块
- 执行构造块
- 执行构造器
- 方法中的普通代码块
11、包、import
11.1、包
标准:
- 包名小写
- 域名倒写
- 用 . 分隔
- 不能使用关键字
- 包声明位置一般在非注释代码的第一行
11.2、import
- 使用不同类的,需要导包
- 导包以后,还想用其他包下同名的类,需要手动写包。
- 同一个包下的类无需导包,直接使用
- 在java.lang下的类也无需导包,直接使用
- 可导入*,导入全部的;
特殊用法:
通过 import static java.lang.Math.*; 导入Math中所有静态方法,直接调用静态方法名使用。
12、面向对象的三大特征:封装、继承、多态
12.1、封装
封装:隐藏内部复杂的具体代码,对外提供简单接口调用。
封装的好处:提高代码的可扩展性、可维护性和安全性。
利用访问修饰符控制外界访问。
实际开发中,利用 private修饰属性,对外提供 setter,getter方法。
12.2、继承
继承:子类继承父类定义的内容。
继承的好处:
- 提高代码的复用性,子类可复用父类代码
- 便于代码的扩展
- 为了多态的使用
继承的特性:
- 父类private修饰的内容,子类实际上也继承,只是因为封装的特性阻碍了直接调用,但是提供了间接调用的方式,可以间接调用。
- 一个父类可有多个子类
- 一个子类只能有一个父类(但可以间接地继承其他类)
- 继承具有传递性,所有类都间接或直接继承Object类。
12.2.1、权限修饰符
权限修饰符 | 同一个类 | 同一个包 | 子类 | 所有类 |
---|---|---|---|---|
private | √ | |||
default | √ | √ | ||
protected | √ | √ | √ | |
public | √ | √ | √ | √ |
12.2.2、方法的重写
重写:子类重写父类方法,参数名和参数列表 (个数、类型、顺序) 必须相同。
12.2.3、重写和重载区别
英文 | 位置 | 修饰符 | 返回值 | 方法名 | 参数列表 | 抛出异常 | |
---|---|---|---|---|---|---|---|
重载 | overload | 同一类中 | 无关 | 无关 | 相同 | 必须不同 | 无关 |
重写 | override | 子类父类中 | 父类小于等于子类 | 父类返回值类型大于等于子类 | 相同 | 相同 | 父类异常大于等于子类 |
12.2.4、super
super修饰属性和方法:显式地调用父类的属性和方法。
(在父类属性或方法与子类重名时,只能通过super.属性或方法调用)
所有构造器的第一行默认情况下都有super(),但是一旦构造器中显式地使用super调用了父类构造器,那么这个super()就不会给你默认分配了。
如果构造器中没有显式地调用父类构造器的话,那么第一行都有super(),调用父类空构造器,可以省略不写。
12.2.5、继承条件下构造器执行过程
12.2.6、Object类
所有的类都直接或间接的继承自Object类。
如果类未声明继承的父类,则默认extends Object。
12.2.6.1、toString()
return getClass().getName() + “@” + Integer.toHexString(hashCode());
返回:全路径名 + “@” + 将对象地址进行哈希算法,并转化为十六进制再转化为字符串
一般来说,有需求的话,可对 toString()方法重写。
12.2.6.2、equals()
顶级父类Object的equals比较的是地址(==)。
一般来说都会重写equals方法
12.2.6.3、链接到面试:为什么重写equals要重写hashCode?
原因:
- 根据hashCode规则,相同对象必须有相同哈希值,因此重写equals一定要重写hashCode。
- 对于自定义的类,比如Student类,一般两个new Student内存地址不一样,而默认hashCode是根据对象的内存地址来计算得到的,两者会不相等,在实际开发中呢,比如只要学号相等我就认为是同一个学生了,这时就要让hashCode根据学号计算生成。
- 对于一些集合,例如HashSet(要通过hashCode计算哈希值,再通过表达式计算所处位置,最后通过equals比较是否相等。) 如果只重写一个,很可能两者会不一致,如下▽
特别是自定义的类,比如new Student(1,“张三”)和new Student(1,“张三”),这两个对业务来说是相等的,但是如果未重写其中一个就会导致不一致,因为HashSet底层是HashMap(key有许多地方都会比较hashCode和equals)
12.2.6.4、类与类的关系
- 继承关系
一个类继承另一个类,通过extends标识
- 实现关系
一个类实现一个interface接口,通过implements标识
- 依赖关系
一个类A使用到另一个类B,类B作为参数被类A在某个方法中使用
- 关联关系
比依赖更强的关系,类B作为类A的属性被使用
- 聚合关系
比关联更强的关系,整体与部分的可分离关系,如公司和员工
- 组合关系
比组合更强的关系,整体与部分的不可分离关系,如人和眼睛、心脏、大脑等
几种关系所表现的强弱程度依次为:组合>聚合>关联>依赖。
12.3、多态
多态:相同的行为,在不同子类中表现出不同的行为特性。
多态的体现:
- 继承
- 实现
- 重写
- 向上、向下转型
总结:父类引用指向子类对象
多态的好处:
- 提高代码的灵活性、扩展性
- 开闭原则:对扩展开放,对修改关闭
12.3.1、多态的案例
父类:Person
public class Person {
protected String name;
protected Integer age;
public Person(){
}
public void eat(){
System.out.println("Person:" + name + ",eat");
}
}
子类:Student
public class Student extends Person{
private String sno;
public Student(String sno){
// super();
this.sno = sno;
}
public void study(){
System.out.println("Student:" + name + ",study");
}
}
测试代码:
总结:
Person stu = new Student(“11812”);
左侧:编译期的类型 = 右侧:运行期的类型
只能使用父类的方法,不能使用子类独有的方法
(如果要使用子类独有的方法,可构造子类对象、向下转型为子类)
12.3.2、继承和泛化
继承:先有父类,后有子类。
泛化:先有子类,后有父类。
13、final
final修饰变量:变量值不可修改,变成了常量,一般用大写字母表示。
final修饰引用类型:这个引用类型的地址不可更改,其内容是可更改的。
final修饰方法:这个方法不能被子类重写
final修饰类:这个类不能被继承,没有子类;(其中方法也没必要用final修饰,因为都不能被继承,就不必考虑方法是否能被子类重写)
14、抽象类和抽象方法
14.1、简介
抽象类:抽象出主要的行为和特征为一个抽象类(抽象类可定义n个抽象方法)
抽象类设计目的:抽象类设计的初衷就是给子类继承用的
抽象类的作用:定义一个子类通用的模板,给子类添加某些约束和限制,使子类设计更加规范。
抽象类案例:
// 如果一个类中有一个抽象方法,那么这个类也要变为一个抽象类
public abstract class Person {
protected String name;
protected String sex;
// 如果有子类对这个方法很满意,则无需重写,直接使用。
public void say(){
System.out.println("say");
}
// 子类永远不满意父类方法,必须重写。
// 没有方法体且被abstract修饰,为抽象方法
public abstract void eat();
public abstract void sleep();
}
// 抽象类可被其他类继承
// 子类继承抽象父类,一般重写父类的所有抽象方法
// 如果没有重写父类的所有抽象方法,则子类也可变为一个抽象类
class Student extends Person{
@Override
public void eat() {
System.out.println("student eat");
}
@Override
public void sleep() {
System.out.println("student sleep");
}
}
// 一个类继承抽象类,也可变为抽象类
abstract class Emp extends Person{
}
14.2、链接到面试
14.2.1、抽象类不能创建对象,那么抽象类是否有构造器
抽象类有构造器,子类初始化对象调用构造器默认调用super,先调用父类的构造器。
14.2.2、抽象类是否可被final修饰
不可以,抽象类的定义就是为了给子类继承,规范子类,而被final修饰的类不能被继承。
15、接口
一个类实现一个接口,那么就要重写接口中所有的抽象方法。(否则只能成为抽象类)
15.1、JDK1.8之前
接口只有两部分内容:
- 常量:默认添加 public static final
- 方法:默认添加 public abstract
[访问修饰符] interface 接口名 [extends 父接口1、2……]{
定义常量;
定义方法;
}
示例:
public interface InterfaceTest {
/*public static final*/ String S = "s";
/*public abstract*/ void test();
}
15.2、JDK1.8之后
新增一部分内容:
- 非抽象方法:public default 修饰的非抽象方法
- 注意:
- 1、default 必须有
- 2、重写default修饰的方法时,实现方法上不能加default,否则报错
- 注意:
- 静态方法:
- 注意:
- 1、static必须有
- 2、静态方法不能被实现
- 注意:
15.2.1、非抽象方法的作用
非抽象方法的作用:在实现此接口较多的类时,添加共同的行为。(为了避免新增一个抽象方法,导致所有实现类都要重写。简而言之,避免繁琐的重复实现)
非抽象方法的实际目的:为了接口升级,方便修改接口。
15.2.2、静态方法的作用
静态方法的作用:为接口提供与接口相关的工具方法。
静态方法的实际目的:为了接口升级,方便修改接口。
15.3、接口和抽象类的区别
1、接口无构造器,抽象类有构造器。
2、接口是用来实现的,抽象类是用来继承的。实现能多实现,继承只能单继承(接口可以多继承)。
3、jdk1.8之前接口只有public static final修饰的变量以及public abstract修饰的方法,jdk1.8之后新增静态方法和public default修饰的非抽象方法。抽象类可以有被其他修饰符修饰的变量和独有的(不被abstract修饰的)方法。
4、接口无构造块和静态代码块,抽象类可以有。
16、内部类
类中类。
16.1、成员内部类
位置:在类中,方法外。
16.1.1、非静态成员内部类
具体如下:
public class Outer {
int age = 20;
class Inner {
int age = 10;
String name;
public void innerMethod() {
// 内部类访问当前类方法
outerHello();
int age = 33;
// 内部类访问外部类
System.out.println(age);
System.out.println(this.age);
System.out.println(Outer.this.age);
}
}
public void outerHello(){
System.out.println("outerHello");
}
public void outerMethod(){
// 外部类要访问内部类,需要创建内部类对象访问
Inner inner = new Inner();
inner.innerMethod();
}
public static void main(String[] args) {
// Outer outer = new Outer();
// Outer.Inner inner = outer.new Inner();
// inner.innerMethod();
Outer.Inner inner = new Outer().new Inner();
inner.innerMethod();
}
}
效果:
16.1.2、静态成员内部类
特点:
- 静态成员内部类可以当作普通类使用
- 静态成员内部类只能访问外部被 static 修饰的属性和方法
如下图:静态类访问外部类的非静态属性和方法报错。
16.2、局部内部类
位置:方法内。
局部内部类访问的局部变量必须被final修饰。
原因:局部变量会随着方法调用结束而消失,但是局部内部类可能因为使用的对象存在而没有消失,所以为了避免因局部变量消失,而导致访问了已经销毁的变量出现问题,局部内部类访问的局部变量必须被final修饰。
16.2.1、如果类在项目中只使用一次,那么直接使用内部类或者匿名内部类即可
public class LocalClassTest {
int num = 0;
public Comparable<LocalClassTest> getComparable() {
return new Comparable<LocalClassTest>() {
@Override
public int compareTo(LocalClassTest o) {
return LocalClassTest.this.num > o.num ? 1 : LocalClassTest.this.num == o.num ? 0 : -1;
}
};
}
}