面向对象初级


面向对象

对象在内存中的存在形式

对象也是一个引用数据类型.

img

对象创建的流程分析

class Person{
    int age = 90;
    String name;
    public Person(String n,int a){
        name = n;
        age = a;
    }
}
//main中
Person p = new Person("jack",20);
  1. 在方法区中加载Person类信息(Person.class), 只会加载一次
  2. 在堆中分配空间(地址)
  3. 完成对象的初始化[3.1 默认初始化 age = 0 name = null 3.2 显式初始化 age = 90 name = null 3.3 构造器的初始化 age = 20 name = jack(字符串,堆中的对象的name地址指向在常量池中jack)]
  4. 对象在堆中的地址, 返回给p(p是对象名, 也可以理解成是对象的引用)

对象分配机制

Person p1 = new Person();
a1.age = 10;
a1.name = "小明";
Person p2 = p1;//把p1赋给了p2 , 让p2指向p1
System.out.println(p2.age);
//p2,age是什么?

img

匿名对象

public class Test{   //公有类
    int count = 9;  //属性
    public void count1(){  //Test类的成员方法
        count = 10; //这个count就是属性 改成10
        System.out.println("count1=" + count); //10
    }
    public void count2(){    //Test类的成员方法
        System.out.println("count2=" + count++);
    }
    //这是Test类的main方法, 任何一个类,都可以有main
    public static void main(String args[]){
        //1. new Test() 是匿名对象, 匿名对象使用后, 就不能再使用(一次性)
        //2. new Test().count1() 创建好匿名对象后, 就调用count1()
        new Test().count1();
        Test t1 = new Test();
        t1.count2();
        t1.count2();
    }
}

Java内存的结构分析:

  1. 栈: 一般存放基本数据类型(局部类型)
  2. 堆: 存放对象
  3. 方法区: 常量池(常量, 比如字符串), 类加载信息

img

属性(成员变量\字段)细节

  1. 属性的定义语法变量, 示例 : 访问修饰符 属性类型 属性名;
  2. 属性的定义类型可以为任意类型, 可以是基本类型或引用类型.
  3. 属性如果不赋值, 有默认值. 和数组一样. String null / int 0 / short 0 / byte 0 / long 0 / float 0.0 double 0.0 / char \u0000 / boolean false

方法调用机制

当返回后, getSum栈就被释放(销毁)了

img

小结

  1. 当程序执行到方法时, 就会开辟一个独立的空间(栈空间)
  2. 当方法执行完毕, 或者执行到return语句时, 就会返回
  3. 返回到调用方法的地方
  4. 返回后, 继续执行方法后面的代码
  5. 当main方法(栈)执行完毕, 整个程序退出
  6. 基本数据类型, 传递的是值(值拷贝), 形参的任何改变不影响实参(方法传参机制)
  7. 引用数据类型, 传递的是地址(传递也是值, 但是值是地址), 形参的改变会影响到实参(方法传参机制)

方法使用细节

  1. 一个方法最多有一个返回值, [ 如果要返回多个结果 , 返回数组 ]

    public int[] getSum(int n1,int n2){
        //创建一个数组
        int[] resArr = new int[2];
        resArr[0] = n1 + n2;
        resArr[1] = n1 - n2;
        return resArr;
    }
    
  2. 返回类型可以是任意类型, 包括基本类型或引用类型

  3. 如果方法要求有返回数据类型, 则方法体中最后的执行语句必须为 return 值, 而且要求返回值类型必须和 return 值类型一致或兼容(自动转换)

    public double f1(){
                                   //错误,缺少返回语句
    }
    
  4. 如果方法是void, 则方法体中可以没有return语句, 或者 只写return(不能有返回值)

  5. 同一个类中的方法, 直接调用

  6. 跨类中方法A类调用B类方法, 需要通过对象名调用

    class A{
        public void m1(){
            System.out.println("A类中的 m1()被调用");
            B b = new B();   //创建对象
            b.hi();
            System.out.println("A类中的 m1()继续执行");
        }
    }
    
    class B{
        public void hi(){
            System.out.println("B类中的 hi()被调用");
        }
    }
    

形参列表的细节

  1. 一个方法可以有0个参数, 也可以有多个参数, 中间用逗号隔开
  2. 参数类型可以是任意类型, 包括基本类型或引用类型
  3. 调用带参数的方法时, 一定对应着参数列表相同类型或者兼容类型(低精度向高精度) 的参数
  4. 方法定义时的参数称为形式参数, 方法调用时的传入参数称为实际参数
  5. 实参和形参的类型要一致或者兼容, 个数, 顺序必须一致

方法重载

java允许方法名相同, 但是要求形参列表不同

返回类型没有要求

可变参数

java允许将同一个类中多个同名同功能但参数个数不同的方法, 封装成一个方法, 通过可变参数实现

class HspMethod{
    //计算2个数的和, 3个数的和, 4.....
    //可以使用方法的重载(但是繁琐)
    
    //使用可变参数封装成方法
    //1.   int...表示接收的是可变参数, 类型是int, 即可以接收多个int(0 - 多)
    //2. 使用可变参数时, 可以当作数组来使用 即nums可以当作数组
    public int sum(int...nums){
           System.out.println("接收的参数的个数:" + nums.length);  
        //遍历求和
        int res = 0;
        for(int i = 0; i < nums.length; i++){
            res += nums[i];
        }
        return res;
    }
}
可变参数的细节
  1. 可变参数的实参可以是数组

    //main
    int[] arr = {1,2,3};
    T t1 = new T();
    ti.f1(arr);
    
    class T{
        public void f1(int...nums){
            System.out.println("长度=" + nums.length);
        }
    }
    
  2. 可变参数的本质是数组

  3. 可变参数可以和普通类型的参数一起放在形参列表, 但必须保证可变参数放在最后

  4. 一个形参列表只能出现一个可变参数

变量作用域

局部变量与全局变量(属性变量)

全局变量可以不赋值, 直接使用, 因为有默认值, 局部变量必须赋值后才能使用, 因为没有默认值

作用域细节
  1. 属性和局部变量可以重名, 访问时遵循就近原则

    class Person{
        String name = "jack";
        public void say(){
            //就近原则
            String name = "king";
            System.out.println(name);
        }
    }
    //输出的是king
    
  2. 属性生命周期较长, 伴随着对象的创建而创建,伴随着对象的销毁而销毁. 局部变量,生命周期较短, 伴随着它的代码块的执行而创建, 伴随着代码块的结束而销毁. 即在一次方法调用过程中.

  3. 作用域范围不一样

    全局变量 : 被本类使用或者其他类使用(需要通过对象的调用)

    局部变量 : 只能被本类中对应的方法中使用

  4. 修饰符的不同

    全局变量 : 可以加修饰符

    局部变量 : 不能加修饰符

构造器

[修饰符] 方法名(和类名保持一致) (形参列表){
    方法体;
}
//构造器没有返回值
//主要作用是初始化
构造器细节
  1. 一个类可以定义多个构造器, 即构造器的重载

  2. 如果程序员没有定义构造器, 系统会自动给类生成一个默认无参构造器(也叫默认构造器), 比如 Dog(){}

  3. 一旦定义了自己的构造器, 默认的构造器就覆盖了, 就不能再使用默认的无参构造器, 除非显式的定义一下, 即Dog(){}

    Dog dog1 = new Dog();//使用的是默认的无参构造器
    

this关键字

class Dog{
    String name;
    int age;
    public Dog(String name,int age){
        //this.name 就是当前对象的属性name
        this.name = name;
        //this.age 就是当前对象的属性age
        this.age =age;
    }
}
深入理解this

img

虚拟机会给每个对象分配this, 代表当前对象

哪个对象调用, this就代表哪个对象

this细节

  1. this关键字可以用来访问本类的属性, 方法, 构造器

    public void f3(){
        //传统方式(按照的是就近原则访问属性,但是如果在方法中有局部变量,那么这个name就是局部变量,不是属性)
        System.out.println("name=" + name + "num=" + num);
        //也可以使用this访问属性(准确定位到属性)
        System.out.println("name=" + this.name + "num=" + this.num);
    }
    
  2. this用于区分当前类的属性和局部变量

  3. 访问成员方法的语法 : this.方法名(参数列表)

  4. 访问构造器语法 : this(参数列表) 只能在构造器中使用(即只能在构造器中访问本类另外一个构造器) 如果有访问构造器语法 : this(参数列表); 必须放在第一条语句

    class T{
        public T(){
            //这里去访问 T(String name, int age) 构造器
            this("jack",100);
            System.out.println("T() 构造器");
        }
        public T(String name, int age){
            System.out.println("T(String name, int age) 构造器");   
        }
    }
    
  5. this不能在类定义的外部使用, 只能在类定义的方法中使用


文章作者: 冬瓜冬瓜排骨汤
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 冬瓜冬瓜排骨汤 !
  目录