Java笔记5类和对象

69 阅读8分钟

Java程序可以认为是一系列对象的集合 而这些对象通过调用彼此的方法来协同工作

类 calss

类是程序的基本单位 是对象的模板 它描述一类对象的状态和行为 是数据类型 是抽象的 概念的 代表一类事物

对象 object

对象是类的实例 有状态(属性)和行为(方法) 是具体的 实际的 代表一个具体事物 例如 一条狗是一个对象 它的状态有:颜色、名字、品种;行为有:摇尾巴、叫、吃等

创建

  1. 先声明再创建
    Cat cat;//声明对象
    Cat cat = new Cat();//创建
  2. 直接创建 Cat cat = new Cat();

内存分配机制

结构分析

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

流程分析

Cat cat=new Cat();
cat.name="小白";
cat.age=12;
  1. 在方法区加载Cat类信息(属性和方法信息 只加载一次)
  2. 在堆中分配空间 进行默认初始化(看规则)+显式初始化+构造器初始化
  3. 把对象在堆中的地址返回给cat cat就指向对象
  4. 进行指定初始化 比如cat.name="小白"; image.png
//示例
Person p1 = new Person(); 
p1.name = “jack”; 
p1.age = 10;
Person p2 = p1;

image.png

属性/成员变量/字段(field)

定义

属性是类的一个组成部分(状态)
一般是基本数据类型 也可是引用类型(对象,数组)
属性的定义语法同变量访问修饰符 属性类型 属性名;
属性如果不赋值 有默认值 规则和数组一致

int 0,short 0, byte 0, long 0, float 0.0,double 0.0,char \u0000, boolean false,String null

访问

对象名.属性名 cat.name

方法/成员方法(函数)

  • 方法是解决一类问题的步骤的有序组合
  • 方法包含于类或对象中
  • 方法在程序中被创建,在其他地方被引用

方法也是类的组成部分(行为)
方法操作对象内部状态的改变 对象的相互调用也是通过方法来完成 在创建一个对象的时候 至少要调用一个构造器 构造器的名称必须与类同名 一个类可以有多个构造器

定义

访问修饰符 返回值类型 方法名(形参列表) {//方法体 
    语句; 
    return 返回值; 
}
//例如
public void speak() {
    System.out.println("我是一个好人");
}

调用机制

  1. 先在栈中开辟一个main方法的空间
  2. 执行到其他方法时 会在栈中开辟一个独立的空间
  3. 当方法执行完毕,或者执行到return语句时,就会返回,
  4. 返回到调用方法的地方
  5. 返回后,继续执行方法后面的代码
  6. 当main方法(栈)执行完毕,整个程序退出 image.png

好处

提高代码的复用性 将细节封装起来 供其他用户调用

注意事项

  1. 方法不能嵌套定义
  2. 形参实参类型要一致
  3. 一个方法最多有一个返回值
  4. 返回类型可以为任意类型 包含基本类型或引用类型(数组,对象)
  5. 如果方法要求有返回数据类型 则方法体中最后的执行语句必须为return 值; 而且要求返回值类型必须和return的值类型一致或兼容
  6. 如果方法是void 则方法体中可以没有return语句 或者只写return ;
  7. 访问修饰符(作用是控制 方法使用的范围) [有四种: public, protected, 默认, private] 如果不写默认访问
  8. 遵循驼峰命名法 最好见名知义 表达出该功能的意思即可 比如 得到两个数的和 getSum

调用

  • 同类中的方法调用:直接调用
  • 跨类中的方法调用(与修饰符相关):对象名.方法名
public class xuexi {
    //编写一个main方法
    public static void main(String[] args) {
        A a=new A();
        a.print(10);//不在同一类 需要对象.方法名
    }
}
class A{
    public void print(int n){
        System.out.println("输入的数字是"+n);
        print2();//在同一类 直接调用即可
    }
    public void print2(){
        System.out.println("print2调用完成");
    }
}

传参机制

基本数据类型

传递的是值(值拷贝) 形参的改变不影响实参

public class Test {
    public static void main(String[] args) {
        int a=10;
        int b=20;
        AA c=new AA();
        c.swap(a,b);
        System.out.println("main方法交换后的值a="+a+" b="+b);
    }
}
class AA{
    public void swap(int a,int b){
        System.out.println("交换前的值a="+a+" b="+b);
        int temp=a;
        a=b;
        b=temp;
        System.out.println("交换后的值a="+a+" b="+b);
    }
}
/*
    交换前的值a=10 b=20
    交换后的值a=20 b=10
    main方法交换后的值a=10 b=20    
 */

引用数据类型

传递的是地址(地址也是值 但传递的值是地址) 可以通过形参影响实参

public class Test {
    public static void main(String[] args) {
        BB b = new BB();

        int[] arr = {1, 2, 3};
        b.shuzu(arr);
        System.out.print("main中的arr数组 ");
        for (int i = 0; i < arr.length; i++) {
            System.out.print(arr[i] + " ");
        }
        System.out.println();

        Person p=new Person();
        p.name="jack";
        p.age=10;
        b.duixiang(p);
        System.out.println("main中的p.age="+p.age);
    }
}

class Person{
    String name;
    int age;
    }

class BB{
    public void shuzu(int[] arr){//传递数组
        arr[0]=100;
        System.out.print("shuzu中的arr数组 ");
        for(int i=0;i<arr.length;i++){
            System.out.print(arr[i]+" ");
        }
        System.out.println();
    }
    public void duixiang(Person p){//传递对象
        p=new Person();
        p.age=99;
    }
}

/*
    shuzu中的arr数组 100 2 3 
    main中的arr数组 100 2 3 
    main中的p.age=10
 */

递归调用

  1. 执行一个方法时 就创建一个新的受保护的独立空间(栈空间)
  2. 方法的局部变量是独立的 不会相互影响 比如n变量
  3. 如果方法中使用的是引用类型变量(比如数组 对象) 就会共享该引用类型的数据.
  4. 递归必须向退出递归的条件逼近 否则就是无限递归 出现StackOverflowError 死龟了:)
  5. 当一个方法执行完毕 或者遇到return 就会返回 遵守谁调用 就将结果返回给谁 同时当方法执行完毕或者返回时 该方法也就执行完毕

image.png

递归实例

猴子吃桃问题:
有一堆桃子 猴子第一天吃了其中的一半 并再多吃了一个 以后每天猴子都吃其中的一半 然后再多吃一个 当到第10天时 想再吃时(即还没吃)发现只有1个桃子了
问题:最初共多少个桃子?

public class Test {
    public static void main(String[] args) {
        T t=new T();
        int day=1;
        int num=t.peach(day);
        if(num!=-1){
            System.out.println("第"+day+"天有"+num+"个桃子");
        }
    }
}
class T {
    public int peach(int day) {
        if (day == 10) {//第十天只有一个桃子
            return 1;
        } else if (day >= 1 && day <= 9) {//前一天的桃子=(后一天的桃子+1)*2
            return ((peach(day + 1) + 1) * 2);
        } else {
            System.out.println("day在1-10");
            return -1;
        }
    }
}
/*
    第1天有1534个桃子
*/

迷宫问题 汉诺塔问题 八皇后问题 p225

重载

定义

同一个类中 有多个同名方法 但形参列表不一致
egcaculate(int a,int b)caculate(double a,double b)

注意事项

  • 方法名必须相同
  • 形参列表必须不同(个数或顺序至少有一个不同 参数名无要求)
  • 返回类型无要求

可变参数

定义

同一类中多个同名同功能但参数个数不同的方法 封装成一个方法

注意事项

  • 实参可以为0个或任意多个 也可以为数组
  • 本质就是数组
  • 可变参数和普通类型的参数一起放在形参列表 但必须保证可变参数在最后
  • 一个形参列表中只能出现一个可变参数

访问修饰符号 返回类型 方法名(数据类型...形参名)

public class Test {
    public static void main(String[] args) {
        Sum m=new Sum();
        System.out.println(m.sum(1,2));
        System.out.println(m.sum(1,2,3));
        System.out.println(m.sums(1,2));
        System.out.println(m.sums(1,2,3));
    }
}
class Sum {
    //方法重载
    public int sum(int a,int b){
        return a+b;
    }
    public int sum(int a,int b,int c){
        return a+b+c;
    }

    //可变参数
    public int sums(int...nums){
        System.out.println("接收的形参个数="+nums.length);
        int res=0;
        for(int i=0;i< nums.length;i++){
            res+=nums[i];
        }
        return res;
    }
}

/*
3
6
接收的形参个数=2
3
接收的形参个数=3
6
 */

有三个方法 分别实现返回姓名和两门课程总分 返回姓名和三门课程总分 返回姓名和五门课程总分 封装成一个可变参数的方法

public class Test {
    public static void main(String[] args) {
        Example s=new Example();
        System.out.println(s.showScore("jack",90.1,80.0));
        System.out.println(s.showScore("terry",90.1,80.0,70.4));
    }
}
class Example {
    public String showScore(String name,double...scores){
        double totalscore=0;
        for(int i=0;i<scores.length;i++){
            totalscore+=scores[i];
        }
        return name+"有"+scores.length+"门课的总分为"+totalscore;
    }
}

/*
jack有2门课的总分为170.1
terry有3门课的总分为240.5
 */

作用域

  • 主要的变量有属性(成员变量)和局部变量(在成员方法中定义的变量)
  • 作用域分为两种
  1. 全局变量(属性):作用域为整个类 有默认值 可以不赋值直接使用
  2. 局部变量(除了属性之外的其他变量):作用域为定义它的代码块中 没有默认值 必须赋值后使用

注意事项

    • 属性和局部变量可以重名 访问时遵循就近原则
    • 两个局部变量在同一个作用域中不能重名 如同一个成员方法中
    • 属性生命周期较长 伴随对象创建而创建 销毁而销毁
    • 局部变量生命周期较短 伴随它的代码块的执行而创建 结束而销毁
    • 属性可以在本类使用 或在其他类使用(通过对象调用)
    • 局部变量只能在对应的方法中使用
    • 属性可以加修饰符
    • 局部变量不可以加修饰符

构造方法/构造器

定义

类的一种特殊方法 作用是完成对新对象的初始化

语法

[修饰符] 方法名(形参列表){
    方法体;
}
  • 修饰符可以默认 可以是public protected private
  • 没有返回值 也不能写void
  • 方法名和类的名字必须相同
  • 参数列表和成员方法规则相同
  • 在创建对象时 系统会自动调用该类的构造器完成对象的初始化

注意事项

  1. 一个类可以定义多个不同的构造器 即构造器重载
  2. 构造器是完成对象的初始化 并不是创建对象
  3. 如果程序员没有定义构造器 系统会自动给类生成一个默认无参构造器(也叫默认构造器)
  4. 一旦定义了自己的构造器 默认的构造器就覆盖了 就不能再使用默认的无参构造器 除非显式的定义一下无参构造器  Dog(){}
  5. 构造器传入的数据是局部变量 不是属性
public class Test {
    public static void main(String[] args) {
        Person p1=new Person("king",40);//第一个构造器
        Person p2=new Person("tom");//第二个构造器
        Person P3=new Person();//第三个构造器

        Dog dog=new Dog();//使用的是默认无参构造器
    }
}
class Dog{
    String name;
    //Dog(){}默认构造器 如果自己定义了构造器 默认的无参构造器就被覆盖 不能再使用
    public Dog(String dname){
        name=dname;
    }
    Dog(){}//显式地定义一下无参构造器 就可以使用无参构造器了
}
class Person {
    String name;
    int age;
    public Person(String pname,int page){//第一个构造器 指定人名和年龄
        name=pname;
        age=page;
    }
    public Person(String pname){//第二个构造器 只指定人名
        name=pname;
    }
    public Person(){//第三个构造器 无参构造器 默认年龄18
        age=18;
    }
}

this关键字

public class Test {
    public static void main(String[] args) {
        Dog d1=new Dog("tom",3);
        System.out.println("狗的名字是"+d1.name+"狗的年龄是"+d1.age);
        //狗的名字是null狗的年龄是0
        //因为构造器中的name和age是局部变量 而不是属性
    }
}
class Dog{
    String name;
    int age;

//    public Dog(String dname,int dage){
//        name=dname;
//        age=dage;
//如果能把dname和dage改成name和age就直观了
//    }
    public Dog(String name,int age) {
        //构造器中的name和age是局部变量 而不是属性
        //变量name和age是分配给自己 没有改变属性
        name = name;
        age = age;
    }
}

this关键字表示当前对象的xxx

使用

  • 用来访问本类的属性 方法 构造器
  • 哪个对象调用 this就代表哪个对象
  • 访问成员方法:this.属性名
  • 访问成员方法:this.方法名(参数列表)
  • 访问同类其他构造器:this(参数列表) 只能放在构造器第一句
  • this不能在类定义的外部使用 只能在类定义的方法中使用

定义Person类属性 并提供compareTo比较方法 用于判断一个人是否和另一个人完全相等 提供测试类Test用于测试 若名字和年龄完全一样就返回ture 否则返回false

public class Test {
    public static void main(String[] args) {
        Person p1=new Person("mary",20);
        Person p2=new Person("mary",20);
        System.out.println("p1和p2比较的结果="+p1.equalTo(p2));
        //p1和p2比较的结果=true
    }
}
class Person{
    String name;
    int age;
    public Person(String name,int age){
        this.name=name;
        this.age=age;
    }
    public boolean equalTo(Person p){
        return this.name.equals(p.name)&&this.age==p.age;
    }
}

2.编写类AA 定义方法find 实现查找某字符串是否在字符串数组中 并返回索引 如果找不到 返回-1

public class Test {
    public static void main(String[] args) {
        String[] strs={"marry","jack","tom"};
        AA a=new AA();
        int n=a.find("tom",strs);
        System.out.println("所查找字符串的索引是"+n);
    }
}
class AA{
    public int find(String str,String[] strs){
        for(int i=0;i< strs.length;i++){
            if(str.equals(strs[i])){
                return i;
            }
        }
        return -1;
    }
}