文章目录
面向对象
面向对象01:什么是面向对象
- 面向对象编程(Object-Oriented Programming,OOP)
- 面向对象的本质:以类的方式组织代码,以对象的组织(封装)数据
- 面向对象具有抽象、封装、继承、多态等特性
- 认识角度:先有对象后有类。对象是具体的事物。类是抽象的,是对对象的抽象
- 代码运行角度:先有类后有对象,类是对象的模板
- 将复杂的业务逻辑简单化,增强代码复用性
面向对象02:回顾方法的定义和调用
public class Demo01 {
//main方法
public static void main(String[] args) {
}
//static修饰的方法与类一同加载
public static void a() {
b();//报错,非静态方法在类实例化后才可经对象调用
}
private void b(){}
}
public class Demo02 {
//引用传递
public static void main(String[] args) {
Person person = new Person();
System.out.println(person.name);//null
Demo02.change(person);
System.out.println(person.name);//sss
}
public static void change(Person person) {
//person是一个指向类的对象
person.name = "sss";
}
static class Person {
String name;
}
}
面向对象03:类与对象的创建
在Java中万事万物都是对象。尽管一切都是对象,但是可操纵都是一个对象的引用。有一个对象引用,但不一定需要一个对象与之关联。
Car carKey;//一个对象引用
这里创建的只是引用,而并非对象,但是如果你想要使用carKey这个引用时,会返回一个异常,告诉你需要一个对象来和这个引用进行关联。一种安全的做法是,在创建对象引用时同时把一个对象赋给它。
Car carKey = new Car();
- 使用new关键字创建对象
- 使用new关键字创建的同时,除了分配内存空间外,还会给创建好的对象进行默认的初始化即对类中构造器的调用。
- 类中的构造器也称构造方法,是在创建对象时必须调用的,具有以下特点:
- 方法名与类名相同
- 无返回类型,也不能写void类型
IDEA :alt+insert 快捷创建构造方法
IDEA : 在 Project Structure 中添加out目录查看class文件
构造方法
- 在Java中的一种特殊的方法,也被称为构造函数、构造器等。在Java中通过提供这个构造器来确保每个对象都被初始化。构造方法只能在对象的创建时期调用一次。
public class Person {
//一个类什么都不写也会存在默认构造方法
String name;
//无参构造
public Person() {
this.name = "null";
}
//有参构造
public Person(String name) {
this.name = name;
}
//new实例化一个对象,本质调用构造方法
Person person = new Person("zz");
}
初始化顺序
- 静态属性:static开头定义的属性
- 静态方法快:static {} 包含的代码块
- 普通属性:非static定义的属性
- 普通方法快:{} 包起来的代码块
- 构造函数:类名相同的方法
- 方法:普通方法
public class LifeCycle {
//静态属性
private static String staticField = getStaticField();
//静态方法快
static {
System.out.println(staticField);
System.out.println("静态方法块初始化");
}
//普通属性
private String field = getField();
//普通方法快
{
System.out.println(field);
}
//构造函数
public LifeCycle() {
System.out.println("构造函数初始化");
}
//静态方法
public static String getField() {
String field = "Field Initial";
return field;
}
//主函数
public static void main(String[] args) {
new LifeCycle();
}
}
面向对象04:创建对象内存分析
- 堆中存放具体创建的对象
- 栈中存放方法和应用变量名
- 方法区加载类的模板
面向对象05:小结
-
类是一个模板,一个抽象。对象是一个具体的实例
-
方法:定义和调用
-
除八大基本类型外都是引用类型,对象是通过引用来操作的:栈—>堆
-
属性:字段(Field) 成员变量
默认初始化:
数字:0 0.0
char:u0000
boolean:false
引用:null
修饰符 属性类型 属性名 = 属性值
-
对象的创建和使用
- 必须使用new关键字创建对象,同时调用构造器
- 对象的属性
- 对象的方法
-
类包含:
静态的属性
动态的方法
面向对象06 :封装
-
封装又称为访问控制权限,它是面向对象三大特性中的一种
-
访问控制权限的核心:只对需要的类可见
-
类中私有属性通过get/set方法操作
-
Java中成员的访问权限共有四种,分别是**private、protected、public、default:
可见性 | private | default | protected | public |
---|---|---|---|---|
同一类 | √ | √ | √ | √ |
同一包中的类 | √ | √ | √ | |
子类 | √ | √ | ||
其他包中的类 | √ |
alt + insert 自动生成get/set方法
隐藏类的属性,通过操作接口来访问
- 提高程序安全性
- 隐藏代码的实现细节
- 统一接口
- 增加可维护性
面向对象07:什么是继承
-
继承是所有面向对象语言不可或缺的部分。在Java中只要我们创建了一个类,就隐式的继承Object父类
-
Java只有单继承,没有多继承。一个子类只能有一个直接父类,一个父类可以有多个子类。但存在间接继承。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zd9m1C4t-1656386563746)(C:\Users\16339\AppData\Roaming\Typora\typora-user-images\image-20220628094402266.png)]
- 继承的关键字是extends,如上图,如果使用了extends显示指定了继承,Father即为父类,Son即是子类
class Father{}
class Son extends Father{}
继承双方拥有某种共性的特征
class Father {
public void feature() {
System.out.println("父类的特征");
}
}
Class Son extends Father {
//若Son没有重写feature方法,则默认用的是父类的feature方法
}
面向对象08:Super详解
- super访问父类的属性和方法
public class Student extends Person {
private String name = "zhang";
public Student() {
//隐藏的代码:调用父类的构造方法:super()
//调用父类构造方法必须写在子类构造方法的第一行
//this.()调用本类的构造,不可与super()同时调用构造方法
super();
System.out.println("子类构造");
}
}
-
创建对象时先调用父类构造器,默认调用无参构造器
-
super调用父类的构造方法,必须在构造方法的第一个
-
super必须只能出现在子类的方法或构造方法中
-
super和this不能同时调用构造方法
-
this()调用本类的构造
-
super()调用父类的构造
面向对象09:方法的重写
重写需要有继承关系,子类重写父类的方法
- 方法名相同
- 参数列表必须相同
- 修饰符:范围可以扩大但不能缩小:public>protected>default>private
- 抛出的异常:范围可以被缩小,但不能扩大:ClassNotFoundException–>Exception(错误)
- 重写,子类的方法和父类必须要一致,方法体不同
public class B {
public void test() {
System.out.println("B->test()");
}
}
public class A extends B {
public void test() {
System.out.println("A->test()");
}
}
public class Test {
//只可重写非静态方法
public static void main(String[] args) {
//方法的调用只与左边定义的数据类型有关
//父类引用指向子类
B a = new A();
a.test(); //A->test()
}
}
面向对象10:什么是多态
-
多态是方法的多态,属性没有多态
-
实现多态的三个充要条件:
-
继承
-
重写父类方法
-
父类引用指向子类
-
father s1 = new Son();
-
-
不可重写的方法:
-
static静态方法不属于实例对象,属于类,不可被重写
-
final 修饰的方法
-
private方法
-
public class Fruit {
int num;
public void eat() {
System.out.println("eat fruit");
}
}
public class Apple extends Fruit {
@Override
public void eat() {
super.num = 10;
System.out.println("eat" + num + "Apple");
}
public static void main(String[] args) {
Fruit fruit = new Apple();
fruit.eat();//调用子类方法
}
}
- mian方法中,Fruit类型的对象指向了Apple对象的引用,这其实就是多态-> 父类引用指向子类对象,因为Apple继承于Fruit,并且重写了eat方法,所以能够表现出来多种状态的形式。
组合
- 组合就是将对象引用置于新类中即可。组合也是一种提高类的复用性的一种方式。如果想让类有更多的扩展功能,多用组合,少用继承
public class SoccerPlayer {
private String name;
private Soccer soccer;
}
public class Soccer {
private String soccerName;
}
-
代码中SoccerPlayer引用了Soccer类,通过引用Soccer类来达到调用soccer中属性和方法的目的
-
组合和继承的区别主要如下:
特征 | 组合 | 继承 |
---|---|---|
关系 | 组合是一种has-a的关系,可理解为有一个 | 继承是一种is-a的关系,可理解为是一个 |
耦合性 | 组合的双方是一种耦合关系 | 继承双方紧耦合 |
是否具有多态 | 不具备多态和向上转型 | 继承是多态的基础,可实现向上转型 |
时期 | 组合是运行时绑定 | 继承是编译期绑定 |
面向对象11:instanceof 和 类型转换
-
instanceof 是 Java 的一个二元操作符,类似于 ==,>,< 等操作符。
-
instanceof 是 Java 的保留关键字。它的作用是测试它左边的对象是否是它右边的类的实例,返回 boolean 的数据类型。
import java.util.ArrayList;
import java.util.Vector;
public class Main {
public static void main(String[] args) {
Object testObject = new ArrayList();
displayObjectClass(testObject);
}
public static void displayObjectClass(Object o) {
if (o instanceof Vector)
System.out.println("对象是 java.util.Vector 类的实例");
else if (o instanceof ArrayList)
System.out.println("对象是 java.util.ArrayList 类的实例");
else
System.out.println("对象是 " + o.getClass() + " 类的实例");
}
}
//结果:对象是 java.util.ArrayList 类的实例
- 类型转换
//父类:Person 子类:Student
Person obj = new Student();
//父类转换为子类
Student student = (Student)obj;
student.go();
//子类转换为父类,可能丢失自己本来的一些方法
Student students = (Student)obj;
Person objs = students;
//go方法可能丢失
objs.go();
- 父类引用指向子类的对象
- 子类转换为父类,向上转型
- 父类转换为子类,向下转型,强制转换
- 方便代码利用率,减少类的创建
面向对象12:static关键字详解
public class Student {
private static int age;//静态的变量 多线程
private double score; //非静态的变量
public void run() {
go();//非静态方法可以调用静态方法
}
public static void go() {
//run();//静态方法只能调用静态方法
}
public static void main(String[] args) {
go();
//run();
}
}
- 代码块
public class Person {
{
System.out.println("匿名代码块");
}
static {
System.out.println("静态代码块");
}
public Person() {
System.out.println("构造方法");
}
public static void main(String[] args) {
Person s1 = new Person();
}
}
/* 结果:
静态代码块
匿名代码块
构造方法
*/
- 静态导入包
//静态导入包
import static java.lang.Math.random;
import static java.lang.Math.PI;
public class Test {
public static void main(String[] args) {
System.out.println(random());//直接使用random()方法
System.out.println(PI);
}
}
面向对象13:抽象类
-
abstract 定义抽象类和抽象方法,被子类单继承实现抽象方法
-
抽象方法必须放在抽象类中
抽象类中可以没有抽象方法
抽象方法只定义无方法体
一个类有大于等于一个抽象方法就是抽象类
-
子类继承抽象类必须重写抽象方法
-
抽象类不能new出对象
//abstract 抽象类: 单继承
//接口可以多继承
public abstract class Ab {
//约束方法,让子类实现方法
public abstract void doSomething();
public void sum() {
}
}
//子类继承抽象类
public class Bc extends Ab{
@Override
public void doSomething() {
}
}
面向对象14:接口的定义和实现
接口相当于是对外的一种约定和标准,Java中接口是由interface关键字来表示的,可以在内部定义方法,但不实现,接口方法隐式修饰为public abstract
//定义接口
public interface Job{
void fun();
}
接口特征:
- interface接口是一个完全抽象的类,不会提供任何方法的实现,只会进行方法的定义
- 接口中只能使用两种访问修饰符,一种是public,对整个项目可见。一种是default,缺省值,只具有包访问权限
- 接口只提供方法的定义,由实现接口的类来提供方法的实现。实现接口关键字为implement,一个接口可以有多个实现,一个类可以实现多个接口
- 实现类必须重写接口中的方法
class ZCZWriteWell implements Job {
@Override
public void fun() {
System.out.println("well");
}
}
接口默认方法
- 在接口中新增方法,意味着要在实现类中实现新增方法
- 而默认方法可以直接在接口中实现:
public interface Job{
void fun();
//默认方法,接口实现类可以不在重写该方法
default void go() {
System.out.println("default method");
}
}
面向对象15:内部类
内部类就是在一个类的内部定义另一个类,在A中类定义B类,B类就是A类的内部类,相对的,A类就是B类的外部类。
成员内部类
内部类定义在外部类成员的位置
总结
-
内部类直接调用了外部类成员方法,原因是内部类拥有外部类对象的引用。
方式:外部类名.this
-
因此内部类可以直接访问外部类成员变量
public class FirstOuterClass {
//成员内部类,定义的位置处于外部类成员的位置
private class InnerClass {
public void run() {
//内部类拥有外部类对象的引用
//所以可以直接使用外部类的成员
System.out.println(FirstOuterClass.this);
}
}
//外部类的非静态方法创建内部类对象
public InnerClass getInnerClassObj() {
return new InnerClass();
}
public static void main(String[] args) {
//创建外部类对象
FirstOuterClass firstOuterClass = new FirstOuterClass();
//1.外部类的非静态方法构造内部类
InnerClass innerClassObj = firstOuterClass.getInnerClassObj();
System.out.println(innerClassObj);
//2.借助外部类对象构造内部类
InnerClass innerClass = firstOuterClass.new InnerClass();
System.out.println(innerClass);
System.out.println(firstOuterClass);
innerClassObj.run();
}
}
成员内部类应用场景
- 为了隐藏并控制对这个类的访问
- 为了将一个类定义在另一个类的内部:当一个类依托另一类存在时
- 借助内部类实现多继承,内外部类都继承不同的类
静态内部类
- 成员内部类使用static修饰即静态内部类
- 静态内部类不能访问外部类中的非静态成员
- 静态内部类不依赖外部类对象存在,无法拥有外部类对象的引用,构造一个静态内部类无需外部类来构造
- 静态内部类中可以有静态成员,成员内部类不能有静态成员
- 创建一个静态内部类对象语法:new 外部类名.静态内部类名();
public class FirstOuterClass4 {
private int a = 0;
//静态成员
private static int b = 1;
//静态内部类
public static class FirstInnerClass {
public FirstInnerClass() {
System.out.println(b);
}
}
//返回内部类对象
public FirstInnerClass getFirstInnerClassObj() {
return new FirstInnerClass();
}
public static void main(String[] args) {
//直接构造静态内部类对象
FirstInnerClass firstInnerClass1 = new FirstInnerClass();
//创建一个外部类对象
FirstOuterClass4 firstOuterClass4 = new FirstOuterClass4();
FirstInnerClass firstInnerClass2 = firstOuterClass4.getFirstInnerClassObj();
}
}
应用场景
- 当一个类是另一个类(外部类)的附属类,它不依赖外部类对象而存在,并且需要单独在外部类之外使用,此时可以创建为静态内部类。
局部内部类
- 定义在局部代码块或方法中
- 只能在局部作用域内new出局部内部类的对象
- 特点:
- 局部内部类只能在其作用域中被实例化,不能用外部类对象实例化一个局部内部类对象。
- 局部内部类不能去使用public、private,因为局部内部类不是外部类的成员
- 局部内部类可以访问外部类对象的成员、拥有外部类对象的引用
在封闭范围内(局部内部类)使用这个范围外的局部变量只能使用常量。
注意:局部内部类中可以去定义局部变量,如果要使用它之外的局部变量,此时编译器按照final类型处理。
-
产生此限制的原因:
由于局部变量和局部内部类生命周期不同,所以编译器统一要求,如果局部内部类使用了它之外的局部变量,此时编译器按照final处理
public class FirstOuterClass2 {
//定义一个外部类的成员方法
public void getFirstInnerClassObj() {
int a = 1;
//定义局部内部类,不可使用public、private修饰
class FirstInnerClass {
//构造方法
public FirstInnerClass() {
//得到外部类对象的引用
System.out.println("外部类对象的引用"+FirstOuterClass2.this);
System.out.println(a+1);
}
//普通方法
public FirstInnerClass getInstance() {
return this;
}
}
//不能使用外部类对象构造局部内部类对象
//只能在局部内部类的作用域中new出一个局部内部类对象
FirstInnerClass firstInnerClass = new FirstInnerClass();
FirstInnerClass innerClass = firstInnerClass.getInstance();
System.out.println("局部内部类对象的引用" + innerClass);
}
public static void main(String[] args) {
//构造一个外部类对象
FirstOuterClass2 firstOuterClass2 = new FirstOuterClass2();
firstOuterClass2.getFirstInnerClassObj();
}
}
/* 结果
外部类对象的引用com.pbteach.javase.oop.innerclass.FirstOuterClass2@1b6d3586
2
局部内部类对象的引用com.pbteach.javase.oop.innerclass.FirstOuterClass2$1FirstInnerClass@4554617c
*/
匿名内部类
- 匿名内部类是一种特殊的局部内部类,没有名字。
- 定义格式:
new 父类或接口(){
//写类的成员
//重写父类或接口中的方法
};
- 特点:
- 没有名字
- 特殊的局部内部类,不能使用private、public等权限修饰
- 由于没有名字,不能定义构造方法,但可使用构造代码块实现构造方法的功能
- 无法通过外部类对象进行实例化,可以访问外部类对象的引用
- 匿名内部类要么继承一个父类,要么实现一个接口,两者不能兼备
- 匿名内部类可以访问外部类对象的成员
- 只允许使用他之外的final常量
//成员内部类
class FirstInnerClass {
//构造方法
public FirstInnerClass() {
System.out.println("外部类对象的引用" + FirstOuterClass3.this);
}
//普通方法
public FirstInnerClass getInstance() {
return this;
}
}
//定义匿名内部类,是FirstInnerClass的子类
FirstInnerClass firstInnerClass = new FirstInnerClass() {
@Override
public FirstInnerClass getInstance() {
System.out.println("匿名内部类");
System.out.println(this);
return this;
}
};