高淇Java300集

面向对象基础

创建对象分为如下四步:

  1. 分配对象空间, 将对象成员初始化为0或null或False
  2. 执行属性值显式初始化
  3. 执行构造方法
  4. 返回对象的地址给的相关变量

1. 对象和类的关系, 属性, 成员变量, 方法

package com.learning;

public class Sxtstu {
    //属性fields
    int age;
    String name;
    int id;


    Computer comp;
	//方法
    void study(){
        System.out.println("I'm studying now" + comp.brand);
    }
    void play(){
        System.out.println("I'm playing now");
    }
    //构造方法,用于创建这个类的对象,无参的构造方法可以由系统自动创建,方法必须和类名保持一致
    Sxtstu(){
        
    }
    //程序执行入口,必须要有
    public static void main(String[] args) {
        Sxtstu stu = new Sxtstu(); //去掉了构造方法
        stu.id =10001;
        stu.sname = "Wang";
        stu.age =18;
		//建好一个对象
        Computer c1 = new Computer();
        c1.brand = "Lenovo";
		//把刚刚建好的对象赋给stu的comp属性
        stu.comp = c1;
        stu.play();
        stu.study();
    }
}

class Computer{
    String brand;
}

2. 一个典型的类的定义和UML图

可以有多个类, 但只能有一个public修饰的类,

图先鸽了

3. 内存分析

(1)栈Stack
  1. 栈描述的是方法执行的内存模型,每个方法被调用的都会创建一个栈帧(存储局部变量, 操作数,方法出口等)
  2. JVM为每一个线程创建一个栈,用于存放该线程执行方法的信息(实际参数[与形参呼应], 局部变量等)
  3. 栈 的信息为线程私有, 线程间不可共享
  4. 栈的存储特性是先进后出, 后进先出
  5. 栈由系统自动分配, 速度快! 栈是一个连续的存储空间
(2)堆Heap
  1. 堆用于存储创建好的对象和数组(数组也是对象)
  2. JVM只有一个堆, 被所有线程共享
  3. 堆是一个不连续的存储空间, 分配灵活, 速度慢!
(3)方法区
  1. JVM只有一个方法区, 被所有线程共享
  2. 方法区也是堆, 只是用于存储类&常量相关的信息
  3. 用来存放程序中永远不变或唯一的内容(类信息[class对象], 静态变量, 字符串常量等)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-PsD87Hhd-1585902761483)(C:\Users\Yeseung\Videos\Captures\064_内存分析详解_栈_堆_方法区_栈帧_程序执行的内存变化过程.mp4 - PotPlayer 2020_3_22 20_26_34 (2)].png)

程序执行的步骤和说明

  1. javac 编译文件Sxtstu.java → \rarr java Sxtstu 虚拟机执行这个类, 调java时启动虚拟机, 就启动空间了:栈/堆/方法区 → \rarr 执行Sxtstu这个类,代码加载到空间中

  2. 执行Sxtstu这个类, 代码加载到空间中, 方法区有类的相关信息了

    代码
    静态变量
    静态方法
    字符串常量 (引号引起来的那些都算)

    有了这些信息以后, java Sxtstu这个语句就算执行完了

  3. 开始找main方法,执行:

    1. 在栈中开辟一个栈帧(🐕), 定义了一个(局部)变量stu–一个引用类型的变量,且引用的是Sxtstu类,目前为空(⭐)

    2. 等号右边是new Sxtstu(), 建一个对象,调用类中的构造器(也是一个方法) → \rarr 开辟一个新的栈帧(🐕🐕) → \rarr 执行这个栈帧,执行的过程中就是通过这个方法来创建Sxtstu类的一个对象 → \rarr 执行完毕之后,堆中就有一个刚刚new出的对象(有地址⭐⭐)了, 删除栈帧(🐕🐕), 有很多属性和方法

      属性/方法类型默认情况
      id数值0
      sname引用类型null
      age数值0
      comp引用类型null
      study()引用类型null
      play()引用类型null
    3. 把地址 ⭐⭐ 赋值给栈帧中的stu(以后任何时候写到stu都知道是⭐⭐ )

    4. 开始给⭐⭐ 这个地址的对象中的属性赋值, 数值就给数值, 引用型就给方法区的字符串

    5. 新建局部变量c1也在main方法这个栈帧(🐕)里面,目前是null, new一个Computer → \rarr 调用构造器, 开辟一个新的栈帧(🐕🐕🐕),会在堆里面创建新的对象, 也有一个地址(⭐⭐ ⭐), 里面只有一个属性brand(默认是null),然后把这个地址给到main方法中的局部变量c1,最后删掉栈帧(🐕🐕🐕)

    6. 给c1.brand赋值’联想’(从方法区来)

    7. stu.comp = c1, 把c1的值赋给comp属性,相当于是把地址(⭐⭐ ⭐)传给了comp

    8. 执行play()方法

    9. 执行study()方法,要找到stu.comp → \rarr 找到了(⭐⭐⭐),执行完毕

  4. 删掉栈帧(🐕)

  5. 虚拟机停掉, 内存没啦, 彻底执行完啦

4. 构造器/构造方法(Constructor)

特点

  1. 通过new关键字调用
  2. 构造器虽然有返回值, 但不能定义返回值类型(返回值的类型肯定是本类), 不能在构造器中return返回某个值
  3. 如果我们没有定义构造器, 则编译器会自动定义一个无参的构造函数, 如果已定义编译器不会自动添加!
  4. 构造器的方法名必须和类名一致
class Point{
    //声明成员变量
    double x,y; 
    // 这就是一个构造方法
    public Point (double _x, double _y){
        x = _x;
        y = _y;
        //这里可以加return;没有参数跟着的话,仅表示结束方法的运行
    }
    public double getDistance(Point p){
        return Math.sqrt((x - p.x)(x - p.x) + (y - p.y)(y - p.y));
    }
}

public class TestConstructor{
    public static void main (String args){
        //调的时候, 把 3.0, 4.0传给了_x, _y
        //把建好的Point的对象的地址赋给p
        Point p = new Point(3.0, 4.0);
        //调的时候, 把 0.0, 0.0传给了_x, _y, 通过构造方法把_x, _y传给了成员变量(属性)x, y
        Point origin = new POint(0,0, 0.0);
        System.out.println(p.getDisatnce(origin));
    }
}

构造方法的重载

​ 方法名称相同, 形参列表不同

public class User{
    int id;
    String name;
    String pwd;
    
    public User(){}
    
    //如果什么都不写只写一个id, 指的是局部变量(而不是成员变量),如果还要表示成员变量的话, 使用this
    public User(int id, String name){
        super(); //构造方法的第一步永远是super(),即使不写的话, 也会自动执行这个的
        this.id = id; // this表示创建好的对象,等号左边的意思的是创建好的对象的成员变量
        this.name = name;
    }
    public User(int id, String name, String pwd){
        this.id = id;
        this.name = name;
    	this.pwd = pwd;
    }
    
     public static void main (String args){
         User u1 = new User();
         User u2 = new User(101,'Wong');
         User u3 = new User(101,'Wong',123456)
    
}

5. 垃圾回收器

垃圾回收器

  1. 引用计数: 堆中的每一个对象都有一个引用计数, 被引用一次, 计数+1, 被引用变量值变为null则引用计数-1, 直至计数为0, 则表示变成无用对象. 容易被循环引用(互相引用无法识别)
  2. 引用可达: 程序把所有的引用关系看作一张图, 从节点gcroot开始, 寻找节点的引用节点然后递归地继续, 直到找到所有的引用节点,所有的引用节点寻找完毕后, 没有被引用的节点即无用的节点

通用的分代垃圾回收机制

对象有三种状态: 年轻代, 年老代, 持久代(Perm)(在方法区里面)

JVM将堆内存划分为Eden, Survivor, 以及Tenured/Old空间;

067_通用分代垃圾回收详解.mp4 - PotPlayer 2020_3_23 19_53_18

垃圾回收过程:

  1. 新创建的对象, 绝大多数都会存储到Eden中
  2. 当Eden满了(到达一定的比例)不能创建新的对象, 则触发垃圾回收,将无用对象清理掉(使用引用可达法或引用计数法), 然后剩余对象复制到某一个Survivor中例如S1, 同时清空Eden
  3. 当Eden区再次满了, 会将S1中的不能清空的对象存在另一个Survivor中此时是S2,同时将Eden中不能清空的对象也复制到S2中,保障Eden和S1均被清空
  4. 重复多次(默认是15次)Survivor中没有被清理的对象,则会复制到老年代Old(Tenured)中
  5. 当Old也满了,则会触发一个Full GC; 之前新生代的垃圾回收称作minorGC
System.gc(); 
## 只是向系统发出请求启动gc()
    

6. This的本质

public class testThis{
    int a,b,c;
    // 重载的构造器
    testThis(int a, int b){
        this.a = a;
        this.b = b;
    }
    
      testThis(int a, int b), int c{
        this(a,b); //调用构造器,必须位于方法里面的第一句
        this.c = c;
    }
    void sing(){}
    void eat(){
        this.sing();
        System.out.println("回家吃饭")
    }
    public static void main(String args[]){
        testThis hi = new testThis(2,3);
    	hi.eat();
    }
}

静态方法中不能使用this, 因为this指的是当前对象

静态变量都在方法区的类信息里面(模具), 里面的放的是类信息而不是对象(对象在堆里面), 更进一步的静态方法中不能调普通的属性和方法

7. static关键字

在类中, 用static声明的变量为静态成员变量, 也成为类变量. 类变量生命周期和类相同, 在整个应用程序执行期间都有效.

强调: static修饰的成员变量和方法从属于类; 普通变量和方法从属于对象.

类似于, 造汽车根据具体的汽车的零件去找图纸上的局部很容易, 但根据一张图纸想要找一辆具体的车上的零件就很难

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-XDroOlqV-1585902761486)(C:\Users\Yeseung\Videos\Captures\067_通用分代垃圾回收详解.mp4 - PotPlayer 2020_3_23 20_32_16.png)]

class User{
    int id;
    String name;
    String pwd;
    static String company = "my company";
    
    public User(int id, String name){
        this.id = id;
        this.name = name;
    }
    
    public void login(){
        printCompany();
        System.out.println("Login:" + name)
    }
    
    public static void printCompany(){
        //login(); 非静态方法,调用会报错
        System.out.println(company);
    } 
    
    public static void main(String args[]){
        User u = new User(101, "高小七");
        User.printCompany();
        User.company = "阿狸";
        User.printCompany();//这两个都可以print,静态变量支持修改吗
}

8. 静态初始化块以及继承树的追溯

先加载类, 再加载构造器

9. Java的参数传值机制

相当于复印一份地址, 传的是复印的那一份, 但指向的是同一个对象,因此也会发生改变;

/*测试参数传递
*/
public class User4{
    int id;
    String name;
    String pwd;
    public User4(int id,String name){
        this.name = name;
        this.id = id;
    }
    public void testParaTransfer01(User4 u){
        u.name = 'Gao'
    }
    public void testParaTransfer02(User4 u){
        u = new User4(200,'Real')
    }
    public static void main(String[] args){
        User4 u1 = new User4(100,'Zhao')
            //把u1的地址传给了Transfer里面的形参u,此时u和u1指向的都是对象u1的地址,testParaTransfer01方法操作了u指向的对象的name属性;  
            u1.testParaTransfer01(u1) 
            System.out.println(u1.name)
            把u1的地址传给了Transfer里面的形参u,此时u和u1指向的都是对象u1的地址,但此时testParaTransfer02方法操作u重新构造了一个属性为200,'Real'的对象,地址也就变成了新开辟的那个地址,与原来的u1没啥子关系了
            u1.testParaTransfer02(u1)
            System.out.println(u1.name)
            
    }
    
}

10.包及包的导入

静态导入

//导入静态属性
import static java.lang.Math.*;

面向对象进阶

继承及instanceOf的应用

package com.wong.oo;

public class Testextends {
    public static void main(String[] args) {
        Student stu = new Student();
        stu.name = "Wong";
        stu.height = 167;
        stu.rest();

        Student stu2 = new Student("sunny",6,"挖掘机");

        System.out.println(stu2 instanceof Person);
        System.out.println(stu2 instanceof Student);
        System.out.println(new Person() instanceof  Student);
    }
}


class Person{
    String name;
    int height;

    public void rest(){
        System.out.println("休息一会儿");
    }
}


class Student extends Person{
    String major;

    public void study(){
        System.out.println("学习两小时");
        }
        //构造器
    public Student(String name, int height, String major){
        this.name = name;
        this.height = height;
        this.major = major;
        }
    //我猜测这个是构造器重载
    public Student(){}
}

类是单继承,只有一个直接的父类(接口可以多继承);

子类继承父类, 可以得到父类的全部属性和方法(构造方法除外), 但不见得可以得到父类的私有属性和方法

如果定义一个类没有调用extends的话父类默认是Object

stu2 instanceof Student 该对象是否是Student类

方法的重写

子类通过重写, 用自身的行为替换父类的行为

package com.wong.oo;

public class Testoverride {

    public static void main(String[] args) {
        Horse h = new Horse(); //前面写Vehicle也行
        h.run();
    }

}

class Vehicle{
    public void run(){
        System.out.println("Runing");
    }

    public void stop(){
        System.out.println("Stop");
    }

    public  Person  whoIsPsg(){
        //返回一个乘客
        return new Person();
    }
}

class Horse extends Vehicle{
    public void run(){
        System.out.println("随便跑跑");
    }
/*不能重写Object, 因为子类在重写父类方法的时候必须要小于原对象类型
    public Object whoIsPsg(){
        return  new Student();

    }*/
  
    public Person whoIsPsg(){//返回值类型要小于等于父类的类型
        return  new Student();

    }
}

重写的三个要点:

'" == " 方法名和形参列表要相同

" ≤ \leq " 返回值类型和声明异常值类型, 子类小于等于父类

" ≥ \geq "子类的访问权限大于父类

Object类的用法, toString方法的重写

package com.wong.oo;

public class TestObject {
    public static void main(String[] args) {
        Object obj;
        TestObject to = new TestObject();
        System.out.println(to.toString());

        Person2 p2 = new Person2("Wong",24);
        System.out.println(p2.toString());
    }

    public String toString(){
        return "Test OOOOObject";
    }
}


class Person2{
    String name;
    int age;
    @Override  //注解表示这是一个重写
    public String  toString(){
        return name + ", age:" + age;
    }

    public Person2(String name,int age){
        this.name = name;
        this.age = age;
    }
}

equals方法的重写

"=="代表双方是否相同, 如果是基本类型则表示值相等, 如果是引用类型则表示地址相等即是同一个对象, 默认的情况是看hash code是否相同

Object类中定义有: public boolean equals(Object obj)方法, 提供定义’对象内容相等’的逻辑

package com.wong.oo;

//Object中默认的equals方法是比较this和参数中的object的hashcode是否相同


import java.net.UnknownServiceException;
import java.util.Objects;

public class TestEquals {
    public static void main(String[] args) {

        Object obj;
        String Str;

        User u1 = new User(1000,"Wong","123456");
        User u2 = new User(1000,"Yeseung","7557");

        System.out.println(u1 == u2);
        System.out.println(u1.equals(u2));

        String str1 = new String("WONG");
        String str2 = new String("WONG");

        System.out.println(str1 == str2);
        System.out.println(str1.equals(str2)); //重写
    }
}



class User{
    int id;
    String name;
    String pwd;

    public User(int id, String name, String pwd) {
        super();
        this.id = id;
        this.name = name;
        this.pwd = pwd;
    }


    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (!(o instanceof User)) return false;
        User user = (User) o; // 强制转型
        return id == user.id;
    }

    @Override
    public int hashCode() {
        return Objects.hash(id);
    }
}
String类型也有equals的重写源码

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值