三、面向对象(高琪java300集+java从入门到精通笔记)

二次信任 提交于 2019-12-01 02:58:25

面向对象基础:

  1. 对象的进化史

    1. 基本数据类型阶段 数据少 无数据管理时代

    2. 数组 数据多了,将同类型的数据放到一起 弱管理

    3. 结构体 数据多了,数据复杂了。将不同类型的数据放到一起 强管理

    4. 对象 数据多了、类型复杂了、行为复杂了。将不同类型的数据放到一起 超强管理

前三个:面向过程 数据和行为是分开的 行为去组织数据

最后一个:面向对象 通过对象组织数据

对象和类:

对象是具体的,类是抽象的(类是对对象的共性的总结)。类也是一个模版,通过类这个模版我们也可以new对象。

对象分为两部分:

静态部分:属性→成员变量

动态部分:行为→方法

类是封装了对象的属性和行为的载体

定义类:

public class 类名 {

//属性

private(建议) 数据类型 属性名; //建议增加相应的getter、setter方法

//方法

(往往是对上面数据的操作,建议static修饰,方法很类似于面向过程中的函数。面向过程中,函数是最基本单位,整个程序由一个个函数调用组成。面向对象中,整个程序的基本单位是类,方法是从属于类和对象的

方法的调用方式:

   对象名.方法名(实参列表)

return 语句终止方法的运行并指定要返回的数据

Java中进行方法调用中传递参数时,遵循值传递的原则(传递的都是数据的副本):

   基本类型传递的是该数据值的copy值。

  引用类型传递的是该对象引用的copy值,但指向的是同一个对象)

//构造方法

}

构造方法(作用:new对象和初始化属性):

  1. 方法名必须跟类名保持一致

  2. 无返回类型(返回本类的对象)

  3. 通过new来调用(实例化对象)

  4. 无参构造器问题:

    1. 如果我们没有手动定义构造器,系统会自动为我们添加一个无参的构造器

    2. 如果我们自己定义了构造器,系统就不会为我们添加无参构造器

  5. 构造方法的第一句总是:super(写不写都是这句),即调用直接父类的构造方法。

(若是构造方法的第一行代码没有显式的调用super(…)或者this(…))

  1. 有继承关系的构造方法调用的顺序

Dog—继承—>Animal—继承—>Object

流程就是:先向上追溯到Object,然后再依次向下执行(new)类的初始化块(构造方法用于对象的初始化!静态初始化块,用于类的初始化操作!)和构造方法,直到当前子类为止。

注:静态初始化块调用顺序,与构造方法调用顺序一样,上溯到Object类,先执行Object的静态初始化块,再向下执行子类的静态初始化块,直到我们的类的静态初始化块为止。

  1. 有参构造方法不能被自动调用,只能依赖super显式地调用。

方法的重载(Overload):

两同(同一个类、同一个方法名)三不同(参数列表不同:;类型、个数、顺序)

返回值不同,构成重载吗? No

形参名称不同,构成重在吗? No

内存分析:Java虚拟机的内存可以分为三个区域:栈stack、堆heap、方法区method area。

栈的特点如下:

1.
栈描述的是方法执行的内存模型。每个方法被调用都会创建一个栈帧(存储局部变量、操作数、方法出口等)

2.
JVM为每个线程创建一个栈(多个线程多个栈),用于存放该线程执行方法的信息(实际参数、局部变量等)

3. 栈属于线程私有,不能实现线程间的共享!

4. 栈的存储特性是“先进后出,后进先出”

5. 栈是由系统自动分配,速度快!栈是一个连续的内存空间!

堆的特点如下:

1. 堆用于存储创建好的对象(new执行完)和数组(数组也是对象)

2. JVM只有一个堆,被所有线程共享

3. 堆是一个不连续的内存空间,分配灵活,速度慢!

方法区(又叫静态区)特点如下:

1. JVM只有一个方法区,被所有线程共享!

2. 方法区实际也是堆,只是用于存储类、常量相关的信息!

用来存放程序中永远是不变或唯一的内容。(类信息【Class对象】(代码一加载)、静态变量、字符串常量等)

class Computer {

String brand;  //品牌

}

public class SxtStu {

//属性fields

int id;

String sname;

int age;

Computer comp;//计算机

//方法

void study() {

    System.out.println("我正在学习!使用我们的电脑,"+comp.brand);

}

void play() {

    System.out.println("我在玩游戏!王王者农药!");

}

//构造方法。用于创建这个类的对象。无参的构造方法可以由系统自动创建。

}

//程序执行的入口,必须要有

Javac Sxtstu.java ,java Sxtstu

public static void main(String[] args) {

    SxtStu stu1 = new SxtStu();//创建一个对象

    stu1.sname = "张三";

    Computer comp1 = new Computer();

     comp1.brand = "联想";

    stu1.comp = comp1;

    stu1.study();

}

}

注意:

  1. 先加载代码到方法区

  2. main方法也是静态方法

  3. 变量是声明(赋值),对象是引用(new存放的是对象的内存地址)

  4. new对象时调用构造方法,也是方法,要开辟新的栈帧(图中没画是因为对象创建完毕)

  5. 类是模板,调用构造方法来根据模板创建对象

  6. stu和c1都是main方法中的局部变量

  7. 对象中的变量起初都是默认值(类的成员变量)

几个关键字:

this(本质:创建好的对象的地址

创建一个对象分为如下四步:

1. 分配对象空间,并将对象成员变量初始化为0或空

2. 执行属性值的显式初始化(方法区中的量赋值??)

3. 执行构造方法(this 可用于构造方法)

4. 返回对象的地址给相关的变量

):

1.普通方法中,调用本方法的对象。

void sing() {

}

void eat() {

    this.sing(); // 调用本类中的sing();

}

2.构造方法中,正要初始化的对象。如下

3.调用其他的构造方法

TestThis() {

    System.out.println("正要初始化一个Hello对象");

}

TestThis(int a, int b) {

    // TestThis(); //这样是无法调用构造方法的!

    this(); // 调用无参的构造方法,并且必须位于第一行!

 this.a = a;

    this.b = b;

}

TestThis(int a, int b, int c) {

    this(a, b); // 调用带参的构造方法,并且必须位于第一行!

    this.c = c;

}

4.this不能用于static方法中(this-对象 static-类信息)

super:

1.super是直接父类对象的引用。可以通过super来访问父类中被子类覆盖的方法或属性。(只能是public和protected)

  1. 使用super调用普通方法,语句没有位置限制,可以在子类中随便调用。(与构造方法中super位置做对比)

static:

用它修饰的变量和方法,就变成了静态变量和静态方法。从属于类。通过类名即可调用。实际存储于方法区中。

  1. 为该类的公用变量,属于类,被该类的所有实例共享,在类被载入时被显式初始化。

2.
对于该类的所有对象来说,static成员变量(局部变量出错)只有一份。被该类的所有对象共享!!

3.
一般用“类名.类属性/方法”来调用。(也可以通过对象引用(不建议)对象.静态成员(是否静态易混淆)或类名(不需要实例化)访问静态成员。)

  1. 在static方法中不可直接访问非static的成员。

堆中对象(汽车)可以得到方法区中类(图纸)

垃圾回收机制(速学堂4.7-4.7.4)

两种无用对象(是没有任何变量引用该对象)情况:对象引用超过其作用范围

{

Example e=new Example();

}

  1. 将对象赋值为null

{

Example e=new Example();

e=null;

}

垃圾回收相关算法

  1. 引用计数法

堆中每个对象都有一个引用计数。被引用一次,计数加1.
被引用变量值变为null,则计数减1,直到计数为0,则表示变成无用对象。优点是算法简单,缺点是“循环引用的无用对象”无法别识别。

循环引用

public class Student {

String name;

Student friend;

public static void main(String[] args) {

    Student s1 = new Student();

    Student s2 = new Student();

    s1.friend = s2;

    s2.friend = s1;        

    s1 = null;

    s2 = null;

}

}

s1和s2互相引用对方,导致他们引用计数不为0,但是实际已经无用,但无法被识别。

2. 引用可达法(根搜索算法)

程序把所有的引用关系看作一张图,从一个节点GC
ROOT开始,寻找对应的引用节点,找到这个节点以后,继续寻找这个节点的引用节点,当所有的引用节点寻找完毕之后,剩余的节点则被认为是没有被引用到的节点,即无用的节点。

内存泄漏:

1.创建大量无用对象

比如,我们在需要大量拼接字符串时,使用了String而不是StringBuilder。

String str = “”;

for (int i = 0; i < 10000; i++) {

str += i;     //相当于产生了10000个String对象

}

  1. 静态集合类的使用

像HashMap、Vector、List等的使用最容易出现内存泄露,这些静态变量的生命周期和应用程序一致,所有的对象Object也不能被释放。

3.各种连接对象(IO流对象、数据库连接对象、网络连接对象)未关闭

IO流对象、数据库连接对象、网络连接对象等连接对象属于物理连接,和硬盘或者网络连接,不使用的时候一定要关闭。

4.监听器的使用

释放对象时,没有删除相应的监听器。

补充:

1.我们无权调用垃圾回收器。

  1. System.gc()强制启动垃圾回收器,该方法只是通知JVM,并不是运行垃圾回收器。尽量少用,会申请启动Full
    GC,成本高,影响系统性能。

    垃圾回收机制只能回收new创建的对象,否则用Object的protected方法finalize()。

package:

package必须位于非注释性第一句。

包名:域名倒着写

import:

引入外部的类

Import static 静态成员 :导入类的静态成员(主要是属性)

final:

修饰变量:常量(命名规范:全部大写,多个单词之间通过下划线隔开)

修饰方法:不能被重写 同private修饰 private final似乎可以被覆盖

修饰类: 不能被继承 所有方法都是final形式 成员变量可final/非final

面向对象的三大特征:

封装

通过private、default 、protected、public关键字实现属性或方法的封装。

在这里插入图片描述

注:其他包中的子类

“高内聚,低耦合”

1.一般使用private访问权限。

  2.

提供相应的get/set方法来访问相关属性,这些方法通常是public修饰的,以提供对属性的赋值与读取操作(注意:boolean变量的get方法是is开头!)。

继承

通过extends 。两个好处:

  1. 代码重用

  2. 通过继承实现对现实世界更加准确的建模

类只有单继承,没有像C++那样的多继承

Java中类没有多继承,接口有多继承。

子类继承父类,可以得到父类的全部属性和方法 (除了父类的构造方法???),

方法重写(Override)的要点(实现多态的必要条件):

1.“==”: 方法名、形参列表相同。

    2.“≤

≤”:返回值类型和声明异常类型,子类小于等于父类(超类、基类、派生类等)。

    3.“≥”: 访问权限,子类大于等于父类

Object类:

  1. 是我们所有类的根基类

  2. toString (用来打印对象)

public String toString() {

 return getClass().getName() + "\@" + Integer.toHexString(hashCode());

}

  getClass()返回对象执行时的Class实例 final 不能被重写

getname()返回类的名称

默认会返回“类名+@+16进制的hashcode”。在打印输出或者用字符串连接对象时,会自动调用该对象的toString()方法

  1. ==、equals、hashcode

  2. “==”比较的是两个对象的引用是否相同(基本类型则表示值相等,引用类型则表示地址相等即是同一个对象)在String
    s1 = "chance"时

在这里插入图片描述

在new String(“chance”)时

在这里插入图片描述

  1. equals()比较的是两个对象的实际内容 ❌ 只是在String类中是

默认就是比较两个对象的hashcode

非String类(即Object中的equals)==和equals一样 都是比较引用地址,即对象

public boolean equals(Object obj) {

return (this == obj);

}

String类中被重写 这才是比较两个对象的实际内容

public boolean equals(Object anObject) {

if (this == anObject) {

return true;

}

if (anObject instanceof String) {

String anotherString = (String) anObject;

…方法将两个String对象拆分成字符数组,然后通过遍历字符数组中的每一个字符是否都相等,若相等,则返回true
否则返回false…

}

1.如果两个对象equals()相同,那么这两个对象的HashCode()一定也相同
equal()对比是绝对可靠的 效率低

2.如果两个对象的HashCode()相同,不代表两个对象就相同,只能说明这两个对象在散列存储结构中,存放于同一个位置

所以解决方式是,每当需要对比的时候,首先用hashCode()去对比,如果hashCode()不一样,则表示这两个对象肯定不相等(也就是不必再用equal()去再对比了),如果hashCode()相同,此时再对比他们的equal(),如果equal()也相同,则表示这两个对象是真的相同了,这样既能大大提高了效率也保证了对比的绝对正确性!

  1. wait、notify、notifyAll final 不能被重写

多态(polymorphism):同一个方法调用,由于对象不同可能会有不同的行为
符合开闭原则(对扩展开放,对修改关闭)

  1. 多态是方法的多态,不是属性的多态(多态与属性无关)。

2.三个必要条件:继承、方法的重写、父类引用指向子类对象(向上转型,属于自动类型转换)

3.父类引用指向子类对象后,用该父类引用调用子类重写的方法,此时多态就出现了。

class Animal {

**public** **void** shout() {

    System.out.println("叫了一声!");

}

}

class Dog extends Animal {

**public** **void** shout() {

    System.out.println("旺旺旺!");

}

**public** **void** seeDoor() {

    System.out.println("看门中....");

}

}

class Cat extends Animal {

**public** **void** shout() {

    System.out.println("喵喵喵喵!");

}

}

public class TestPolym {

**public** **static** **void** main(String[] args) {

    Animal a1 = **new** Cat(); // 向上可以自动转型

    //传的具体是哪一个类就调用哪一个类的方法。大大提高了程序的可扩展性。

    animalCry(a1);//a1为编译类型,Cat对象才是运行时类型。

    Animal a2 = **new** Dog();

    animalCry(a2);//a2为编译类型,Dog对象才是运行时类型。

    //编写程序时,如果想调用运行时类型的方法,只能进行强制类型转换。

    // 否则通不过编译器的检查。

弊端,就是无法调用子类特有的功能,我不能使用父类的引用变量调用Dog类特有的seeDoor()方法

    Dog dog = (Dog)a2;//向下需要强制类型转换

    dog.seeDoor();

}



// 有了多态,只需要让增加的这个类继承Animal类就可以了。

**static** **void** animalCry(Animal a) {

    a.shout();

}

父类引用做方法的形参,实参可以是任意的子类对象,可以通过不同的子类对象实现不同的行为方式。

/\* 如果没有多态,我们这里需要写很多重载的方法。

 \* 每增加一种动物,就需要重载一种动物的喊叫方法。

static void animalCry(Dog d) {

    d.shout();

}

static void animalCry(Cat c) {

    c.shout();

}\*/

}

向下转型时用到 myobject(父类对象引用) instanceof (父类对象是否为子类的实例)
ExampleClass(子类)

动态绑定机制(java慢,现找,下一步做什么不知道,做的时候再说)、静态绑定:

抽象类(abstract):

  1. 包含抽象方法的类,一定是抽象类。

  2. 抽象类不能被new(实例化)。

  3. 抽象类可以包含:普通方法、成员变量、构造方法(不能用,不能new,只能用来被子类调用。)。

类不能用final修饰

方法只能用public修饰

如果子类没有实现父类的抽象方法,则必须将子类也定义为为abstract类

接口:

  1. interface

  2. 类实现接口:implements 可以实现多个接口

  3. 接口可以多继承

interface Inter implements Inter1,Inter2{ //Inter、Inter1、Inter2都为接口

}

  1. 接口定义的一组规范!实现现实世界中这样的逻辑::如果你是…则必须能…

定义接口的详细说明:

    1.

访问修饰符:只能是public(仅限于接口在与其同名的文件中被定义)或默认。

    2. 接口名:和类名采用相同命名机制。
    3. extends:接口可以多继承。
    4.

全局常量(一般是不会在接口中使用):接口中的属性只能是常量,总是:public
static final 修饰。不写也是。

    5. 公共的抽象方法:接口中的方法只能是:public abstract。

省略的话,也是public abstract。

要点

    1. 子类通过implements来实现接口中的规范。
    2. 接口不能创建实例,但是可用于声明引用变量类型。
    3.

一个类实现了接口,必须实现接口中所有的方法,并且这些方法只能是public的。

    4.

JDK1.7之前,接口中只能包含静态常量、抽象方法,不能有普通属性、构造方法、普通方法。

    5. JDK1.8后,接口中包含普通的静态方法。

区别:

  1. 普通类:具体实现
    2. 抽象类:具体实现,规范(抽象方法) 有main 有构造器???
    3. 接口:规范!(全部抽像)接口是抽象类的特殊化 比抽象类更加“抽象”

无main 无构造器???

对于遵循某个接口的抽象类,可以不实现该接口中的抽象方法

内存机制:

  1. 存放局部变量

  2. 不可以被多个线程共享

  3. 空间连续,速度快

  1. 存放对象

  2. 可以被多个线程共享(所以有资源冲突问题 每个对象都有锁)

  3. 空间不连续,速度慢。但是灵活

方法区

  1. 存放类的信息:代码、静态变量、字符串常量等

  2. 可以被多个线程共享

  3. 空间不连续,速度慢。但是灵活

垃圾回收器(GC Garbage Collection):

  1. 程序员不能调用垃圾回收器。但是可以通过System.gc()建议回收。

  2. Finallize(释放对象池资源):一般也不用调

递归算法:

  1. 递归头:什么时候不调用自己

通属性、构造方法、普通方法。

    5. JDK1.8后,接口中包含普通的静态方法。

区别:

  1. 普通类:具体实现
    2. 抽象类:具体实现,规范(抽象方法) 有main 有构造器???
    3. 接口:规范!(全部抽像)接口是抽象类的特殊化 比抽象类更加“抽象”

无main 无构造器???

对于遵循某个接口的抽象类,可以不实现该接口中的抽象方法

内存机制:

  1. 存放局部变量

  2. 不可以被多个线程共享

  3. 空间连续,速度快

  1. 存放对象

  2. 可以被多个线程共享(所以有资源冲突问题 每个对象都有锁)

  3. 空间不连续,速度慢。但是灵活

方法区

  1. 存放类的信息:代码、静态变量、字符串常量等

  2. 可以被多个线程共享

  3. 空间不连续,速度慢。但是灵活

垃圾回收器(GC Garbage Collection):

  1. 程序员不能调用垃圾回收器。但是可以通过System.gc()建议回收。

  2. Finallize(释放对象池资源):一般也不用调

递归算法:

  1. 递归头:什么时候不调用自己

  2. 递归体:什么时候调用自己

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!