面向过程:侧重分步骤,考虑完成该功能的过程(自己一步一步干)
侧重分模块(找人分发任务解决任务)
总结
面向对象优势:可扩展,可维护,灵活性高,程序耦合度低
面向对象缺点:性能比面向过程相对较差
💪 方法特点#
- 静态方法用类名调用,构造方法创建实例化时对象调用,成员方法对象调用
- 构造方法
- 声明:权限修饰符 方法名(参数列表){方法体}; 方法名和类名必须一致
- 类中没有构造方法,默认有一个无参构造,如果自己编写写但默认的就没有了,一般自己写完构造方法会再写一个无参
- 只有成员方法才可以直接访问成员属性,静态方法不能访问成员变量(方法只要需要成员变量就定义为成员方法)
- 成员方法可以访问静态变量
🎗 类和对象之间的关系#
- 类:对事物客观描述的标准
- 对象:一切皆对象,符合类标准的具体个体
对象使用#
实例化对象:Student s1 = new Student();
通过 get 和 set 方法获取和设置成员属性
🔌 JavaBean#
JavaBean
是一种Java
语言写成的可重用组件,它是一个类(类必须是具体的公共的,并且具有无参构造器)
- 私有化成员变量
- 对外提供
get
和set
方法 - 显式声明无参和全参构造
get
和set
方法的作用- 保护成员变量
- 进行拦截
🎉 实例化的内存过程#
- 加载客户端类
main()
方法压栈- 加载服务端类
- 调用构造方法
- 在堆内存中开辟空间,对实例化对象初始化
- 构造方法出栈,实例化对象赋值给变量
💥 异常#
- 编译时异常
- 运行时异常
- 空指针异常(用
null
访问成员变量时) - 数组下标越界
- 类型转换异常
- 空指针异常(用
⚠️ 易错点#
- 区分成员方法和构造方法:看有无返回值(构造方法无返回值)
- 对象可以调用静态变量,
javac
编译时会改成类名调用静态变量(所以空指针也不影响) - 静态方法不能调用成员变量
👈 this#
对象中第一个成员变量,保存了当前对象的内存地址(不能出现在静态方法中)
作用#
- 用在成员方法 / 构造方法中 区分同名的成员变量和局部变量
- 重载调用当前类的有参构造方法
必须在当前构造方法方法体第一行 - 链式调用
⚙️ static#
修饰符 用来标注静态属性 如果没有使用static
为成员属性
功能#
- 静态变量
- 静态方法
- 静态代码块
不能调用 在程序加载时调用 (在main()
方法之前运行) 只执行一次
代码块:无名称方法 、自动调用
访问一个类的静态属性的时候 才会加载该类到内存中
实例语句块 可看做无名字成员方法
📦 封装#
把所有组成部分组合到一起,还可以通过权限控制修饰符将数据隐藏起来,可以控制用户对类数据的修改程度
适当的封装可以让代码更容易理解,易于维护,更提高了代码的安全性
导包#
放在 package
之下 class
之上
静态导入
导入某个类的某个静态变量
权限控制#
范围 | public | protected | default | private |
---|---|---|---|---|
同类 | ✔️ | ✔️ | ✔️ | ✔️ |
同包 | ✔️ | ✔️ | ✔️ | ❌ |
子类 | ✔️ | ✔️ | ❌ | ❌ |
全局 | ✔️ | ❌ | ❌ | ❌ |
🧬 继承#
从一个已有的类中衍生出一个新的类。该类往往包含着父类的特征,还可以新增特有的属性
📝 重写#
必须有继承关系
- 方法名、参数列表、返回值相同
- 不能比原方法有更低的访问权限
- 不能比原方法有更宽泛的异常
重写重载区别#
注解#
源码注解
编译注解
运行时注解
🛑 final#
可放置在
- 类
- 变量
- 方法
🌱 多态#
父类引用指向子类对象
- 父类
- 引用 指向的引用数据类型
- 指向 可以找到谁
- 子类对象 创建一个子类的实例化对象
使用父类创建的引用类型变量 可以找到子类对象
父类 变量名 = new 子类();
向上转型(由子类转换为父类) 类似于自动转换类型
非成员方法
优点#
- 降低耦合度
- 扩展性 替换性 灵活性增强
缺点#
会丢失子类特有的属性
成员方法 如果子类重写了父类的成员方法则调用子类的成员属性
非成员方法 调用父类方法
public class Poly {
public static void main(String[] args) {
Sup sub = new Sub();
// 2
System.out.println(sub.age);
sub.m1();
// Son m2
sub.m2();
// sub.m3();
}
}
class Sup{
int age = 2;
public void m1(){
System.out.println("Father m1");
}
public void m2(){
System.out.println("Father m2");
}
}
class Sub extends Sup{
int age = 1;
public void m2(){
System.out.println("Son m2");
}
public void m3(){
System.out.println("Son m3");
}
}
instanceof#
判断某个对象是否由某个类实例化而来
向下转型(由父类转换为子类)
- 直接多态
父类 变量名 = new 子类();
- 实参形参多态
- 返回值多态
方法返回值使用父类声明
隐蔽多态#
通过子类对象,调用继承父类的成员方法时 此时上下文环境为多态环境
public class Poly_05 {
public static void main(String[] args) {
// Sup sup = new Sub();
// // 2 父类
// System.out.println(sup.age);
// // 父类 因为子类没有
// sup.m1();
// // 子类 因为覆写
// sup.m2();
// // 报错,因为父类没有,多态会丢失子类特有属性
// // sup.m3();
// // 向下转型
// Sub sub = (Sub) sup;
// // 1 子类
// System.out.println(sub.age);
// // 父类 继承
// sub.m1();
// // 子类 因为子类有
// sub.m2();
// // 子类,又没有使用多态
// sub.m3();
Sub sub = new Sub();
sub.m1();
}
}
class Sup {
int age = 2;
public void m1() {
/**
* this : 是对象中第一个成员变量,保存当前对象的内存地址
*
* this既然保存当前对象内存地址,那么this的类型 可以是当前类类型,可以是父类类型
*
* this写在哪个类中,哪个类就是当前类 所以 当前类 是Sup 父类 是 Object
*
* 因为this能调用当前类中所有的属性,并没有丢失,所以this是Sup 当前类类型
*
* Sup this;
*
* this : 哪个对象调用这个成员方法,this就指向谁
*
* 最终是Sub调用的m1方法,所以this 指向 Sub
*
* Sup this = Sub;
*/
// System.out.println("父类m1");
System.out.println(this);
// 2 父类
System.out.println(this.age);
System.out.println(this.sakjdkashdhqwrjnfaksf);
System.out.println(age);
// 子类
m2();
// 报错
// m3();
}
public void m2() {
System.out.println("父类m2");
}
}
class Sub extends Sup {
int age = 1;
public void m2() {
System.out.println("子类m2");
}
public void m3() {
System.out.println("子类m3");
}
}
🐘 抽象#
abstract
修饰符 用来修饰抽象类和抽象方法
abstract
修饰的类是抽象类,并且抽象类不能创建对象而且抽象类一般主要用于被继承
abstract
修饰的方法是抽象方法。该方法没有方法体,不实现功能,用于让不同的子类实现方法
抽象方法必须在抽象类中,而抽象类中可以存在普通方法
抽象类中可看作特殊的类,只不过不能创建对象而已
abstract
和final
不能同时出现
- 子类如果继承了抽象类 那么需要实现所有的抽象方法 否则该子类就需要使用
abstract
修饰 - 抽象类继承抽象类,需要实现 0~N 个抽象方法
- 普通类继承抽象类需要实现所有方法
👄 接口#
interface
关键字
1.8 之前接口是完全抽象的,只允许出现抽象方法和常量 (psf
)
在接口中只有常量没有变量,并且psf
可以省略 且权限控制默认是public
抽象方法的 abstract
可以省略
interface A{
int age = 1;
// 默认方法
default void dmethod(){
System.out.println("默认方法");
}
}
class B{
void m(){
System.out.println(A.age);
}
}
🌌 Object#
Object
是 Java 提供的根类 所有类都直接或间接继承Object
java.lang.Object
在java.lang
包下 核心包下的所有类不需要导入
toString#
equals#
==
基本类型比较值大小,引用类型比较地址
==
是 equal()
的默认底层实现
默认的 equals()
比较地址
finalize#
JVM 跨平台 多线程 面向对象 自动垃圾回收
垃圾 没有任何引用指向该对象的时候,该对象成为垃圾对象
垃圾被回收时 自动调用该对象的 finalize()
方法 是在对象生命结束时候被调用
System.gc()
程序员可以建议 JVM 进行垃圾回收
🔗 类之间关系#
类与类之间的关系详见 这里
🧩 内部类#
成员内部类#
class A{
class B{
}
}
等同于成员变量 不能有静态声明
内部类可以使用权限控制修饰符
可以访问外部类的私有化 (被 private
修饰) 属性
成员内部类可以直接访问外部类的所有数据
此时 B
类的全类名为 A$B
静态内部类#
class A{
private int a = 1;
private static int b = 1;
static class B{
int c = 1;
static int d = 1;
public void method(){
// System.out.print(a);
A aaa = new A();
System.out.print(aaa.a);
System.out.print(b);
}
}
}
- 静态内部类可以等同看作静态变量
- 静态内部类无法直接访问外部类的的成员属性
- 静态内部类可以声明任何数据 (静态 / 成员)
- 创建外部类的对象后可访问外部类的的成员属性
局部内部类#
- 方法中的类是局部内部类
- 局部内部类可看作局部变量
- 如果通过局部内部类访问外部方法中的局部变量的时候,该变量需要加
final
修饰 1.8 后final
可以省略 - 局部内部类不能使用权限修饰符
- 局部内部类不能有静态声明
class A{
void method(){
class B{
}
}
}
此时B
类的全类名为 A$1B
如果内部类名重复 则为外部类类名 $2 内部类类名
匿名内部类#
当一个方法的参数需要传入一个类对象时,可以使用匿名内部类
语法#
new 实现接口(){
//匿名内部类类体部分
}
实现接口 接口没有构造函数所以为空参数
new 父类构造器(实参列表){
//匿名内部类类体部分
}
调用父类构造器可以为空参数,也可以传入参数
示例#
//接口部分
interface Product{
public double getPrice();
public String getName();
}
public class Anonymous{
public void test (Product p){
System.out.println(p.getName()+"--------"+p.getPrice());
}
public static void main(String[] args ){
Anonymous as = new Anonymous();
//此处实现接口并实现抽象方法
as.test(new Product(){
//实现方法
public double getPrice( ){
return 8888;
}
//实现方法
public String getName(){
return "I can do it ";
}
});
}
}
上面代码很简单,就是定义了一个类Anonymous
,在类里定义了一个test
方法。然后就是创建 Anonymous 对象,调用他的实例方法 test ()
不过调用test()
方法时,要传入一个Product
对象。但是由于Product
是一个接口,无法创建对象,所以要实现该接口。因此此处采用匿名内部类的方式进行,并实现接口中全部的抽象方法
作用#
无需通过实现接口或者继承抽象类的方式来实现。但是使用匿名内部类的优点是显而易见的,可以少些代码,而且代码更加简洁