10、Java类与对象

10、Java类与对象

类就是自定义的数据类型,对象就是一个具体的实例

10.1 基本介绍

  • 属性/成员变量/实例字段:是类的一个组成部分,一般是基本数据类型,也可以是引用类型(对象;数组)

    定义:访问修饰符 属性类型 属性名;若不赋值,则有默认值,规则和数组一致

  • Java内存结构分析:

    1. 栈:一般存放基本数据类型(局部变量)
    2. 堆:存放对象,数组等
    3. 方法区:常量池(常量,比如字符串);类加载信息(属性信息,方法信息)
  • Java创建对象流程(简略):

    1. 在方法区先加载类信息(属性和方法信息,且只会加载一次)
    2. 在堆中分配空间,进行默认初始化
    3. 把地址赋给对象
    4. 进行指定初始化,覆盖原来的初始化值
    5. 注意:对象的地址在堆中。对象的引用(对象名)在栈中,指向堆中对应的对象地址
  • Java成员方法的定义:

    访问修饰符 返回数据类型 方法名(形参列表…){方法体;return返回值;}

    1. 访问修饰符:规定访问权限
    2. 返回数据类型:表示成员方法输出,void没有返回值
    3. 方法名:表示成员方法的输入
    4. 方法体:实现该方法的具体实现
    5. return 不是必须的。如void时可以不用return,或者只写return
    6. 一个方法最多有一个返回值,返回类型可以是任何类型,包括引用类型(数组,对象)
    7. 形参列表可以有0个参数,也可以有多个参数;参数类型可以为任何类型,包括引用类型,调用方法时,要对应参数列表传入相同类型或兼容类型的参数
    8. 方法定义时的参数称为形式参数(形参);方法调用时传入的参数称为实际参数(实参)。实参和形参类型,个数,顺序必须一致。
    9. 方法里不能嵌套其他方法的定义
    10. 在两个方法在同一类中,一个方法可以直接调用另一个方法,不用创建对象
    11. 跨类调用方法,需要创建该类对象,再通过对象名调用该类方法;注意:跨类的方法调用和方法的访问修饰符相关;后续会细说
  • Java方法调用机制:

    1. 在栈中自动创建一个main栈,当程序执行到方法时,就会开辟一个独立的栈空间
    2. 当方法执行完毕,或者执行到return语句时,就会返回
    3. 返回到调用方法的地方
    4. 返回后,只需执行之后的语句
  • Java成员方法传参机制

    1. 基本数据类型的传参机制:基本数据类型传递的是值(值拷贝),形参的任何改变不会影响实参
    2. 引用数据类型的传参机制:引用数据类型传递的是地址(传递的也是值,但值是地址),可以通过形参影响实参
  • 练习:编写一个方法copyPerson,可以复制一个Person对象信息,返回复制后的对象;要求两个对象相互独立,只是属性相同

    Person person = new Person();
    person.age = 10;
    person.name = "Jack";
    Tool T= new Tool();
    Person newperson = T.copyPerson(person);
    System.out.println(newperson.age);
    System.out.println(newperson.name);
    System.out.println(person.age);
    System.out.println(person.name);
    
    class Person{
        int age;
        String name;
    }
    class Tool{
        public Person copyPerson(Person p){
            Person newperson =new Person();
            newperson.age = p.age;
            newperson.name = p.name;
            return newperson;
        }
    }
    
  • 递归练习1:给定整数n ,用递归的方式求出斐波那契1,1,2,3,5,8,13…第n个数为多少

    public class Test_0009 {
        public static void main(String[] args) {
            Fibonacci f = new Fibonacci();
            int num = f.fibonacci(8);
            System.out.println(num);
    
        }
    }
    class Fibonacci{
        public int fibonacci(int n ){
            if(n==1||n==2){
                return 1;
            }else{
                return fibonacci(n-1)+fibonacci(n-2);
            }
        }
    }
    
  • 递归练习2: 迷宫问题

    int[][] map = {{1,1,1,1,1,1,1},
                   {1,0,0,0,0,0,1},
                   {1,0,1,1,0,0,1},
                   {1,1,1,0,0,0,1},
                   {1,0,0,1,0,0,1},
                   {1,0,0,0,0,0,1},
                   {1,0,0,0,0,0,1},
                   {1,1,1,1,1,1,1}};
    Maze m = new Maze();
    m.findWay(map,1,1);
    for(int i = 0;i< map.length;i++){
        for(int j = 0;j<map[i].length;j++){
            System.out.print(map[i][j]+" ");
        }
        System.out.println();
    }
    class Maze{
        /**
         * 1. findWay:表示找出迷宫路径
         * 2. 找到返回true
         * 3. i,j表示当前所在位置,初始化为1,1
         * 4。 规定map数组的含义:0表示可以走,1:表示障碍物,2:表示可以走,3:表示可以走,但是走过且走不通
         * 5. 当map[6][5]==2时,说明找到出口,打印此时map,2代表走过的路径
         */
        public Boolean findWay(int[][] map,int m,int n){
            if(map[6][5]==2){
                return Boolean.TRUE;
            }
            else{
                if(map[m][n]==0){
                    map[m][n]=2;
                    if(findWay(map,m+1,n)){
                        return true;
                    }
                    else if(findWay(map,m,n+1)){
                        return true;
                    }
                    else if(findWay(map,m-1,n)){
                        return true;
                    }
                    else if(findWay(map,m,n-1)){
                        return true;
                    }
                    else{
                        map[m][n]=3;
                        return false;
                    }
                }else{
                    return false;
                }
            }
        }
    }
    
  • 递归练习3: 汉诺塔

    Hanoi hanoi = new Hanoi();
    char A = 'A';
    char B = 'B';
    char C = 'C';
    hanoi.move(4,A,B,C);
    class Hanoi{
        //若有多个盘,可以看成两个盘即最下面和上面所有的盘(n-1)
        public void move(int n,char a,char b,char c){
            if(n==1){
                System.out.println(a+"->"+c);
            }
            else{
                //1. 先移动上面所有盘到b塔
                move(n-1,a,c,b);
                //2. 把a塔剩余的一个移动到c塔
                move(1,a,b,c);
                //3. 把b塔所有盘移动到c塔
                move(n-1,b,a,c);
            }
        }
    }
    
  • 递归练习4:八皇后

    EightQueens eq = new EightQueens();
    int num = eq.check(0);
    System.out.println("num="+num);
    
    class EightQueens{
        int[] arr = new int[8];
        int count = 0;
        public int check(int row){
            if(row ==8){
                for(int i =0;i< arr.length;i++){
                    System.out.print(arr[i]+" ");
                }
                System.out.println();
                count++;
            }else{
                for(int column = 0;column< arr.length;column++){
                    arr[row] = column;
                    if(judge(row)){
                        check(row+1);
                    }
                }
    
            }
            return count;
        }
        public Boolean judge(int row){
            for(int i =0;i<row;i++){
                if(arr[row]==arr[i] || Math.abs(row-i) == Math.abs(arr[row]-arr[i])){
                    return false;
                }
            }
            return true;
        }
    }
    

10.2 方法重载(overload)

  • Java中允许同一个类中存在多个同名方法,但要求形参列表(参数类型、个数或顺序至少有一样不同)不一致,只有返回类型不同不能称为方法重载

    方法重载好处:减轻取名的麻烦和记名的麻烦

    例如:System.out.println()方法中方法名相同但是形参列表不同,该方法可以接收不同的数据类型输出

10.3 可变参数

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

  • 基本语法:访问修饰符 返回类型 方法名(数据类型… 形参名){方法体;}

  • 注意事项:可变参数的实参可以为0个或任意个;

    ​ 可变参数本质是数组;

    ​ 可变参数和普通类型参数一起放在形参列表中时,必须保证可变参数在最后

    ​ 一个形参列表中只能出现一个可变参数

    VarParameter vp = new VarParameter();
    int sum = vp.sum(1,2,3,4);//这里可变参数数量为3个
    System.out.println("可变参数和为:"+ sum);
    class VarParameter{
        //计算两个数的和、三个数的和、四个数的和...
        //可以使用方法重载,但是过于繁琐冗余
        //因为方法名相同,功能相同,只是参数个数不同,可以使用可变参数进行封装
        //int... 表示方法接收的是可变参数,即可以接收多个int类型的形参
        //可以当做数组使用
        public int sum(int... n){
            int result = 0;
            for(int i =0 ;i<n.length;i++){
                result +=n[i];
            }
            return result;
        }
    }
    

10.4 变量作用域

  • 主要变量就是属性(成员变量)和局部变量

  • 局部变量一般是指在成员方法中定义的变量和代码块中的变量

  • 全局变量:也就是方法的属性,作用域为整个类体

  • 局部变量:除了属性之外的其他变量,作用域为定义它的代码块中

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

  • 属性和局部变量可以重名,使用时遵从就近原则

  • 属性的生命周期长,伴随着对象的创建而创建,伴随着对象的销毁而销毁;局部变量生命周期较短,即在一次方法调用过程中创建->销毁

  • 作用域范围不同:全局变量(属性):可以被本类使用或其他类(通过对象调用)使用

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

  • 修饰符不同:全局变量(属性)可以加修饰符,也可以不加;局部变量不可以加修饰符

10.5 构造方法/构造器(constructor)

  • 基本语法:[修饰符] 方法名(形参列表){方法体;}

  • 作用:主要完成对象的初始化

  • 构造器修饰符可以是默认的,也可以是public、protected、private

  • 构造器没有返回值

  • 方法名必须和类名一致

  • 参数列表和成员方法规则一致

  • 构造器调用由系统完成

  • 一个类可以定义多个不同的构造器;即构造器重载

  • 若程序员没有定义构造器,系统会自动给类生成一个默认无参构造器(默认构造器),使用javap指令反编译查看:javap 类名

  • 一旦定义了自己的构造器,默认构造器就被覆盖,就不能再使用默认的无参构造器了,除非显式的定义一下

    Constructor constructor1 = new Constructor(16,"Jack");
    System.out.println("age:"+constructor1.age+"\tname:"+constructor1.name);
    Constructor constructor2 = new Constructor(20);
    System.out.println("age:"+constructor2.age);
    Constructor constructor3 = new Constructor("Lucy");
    System.out.println("name:"+constructor3.name);
    Constructor constructor4 = new Constructor();
    class Constructor{
        int age;
        String name;
        //构造器没有返回类型,也不能写void
        //方法名必须和类名一致
        public Constructor(int Page,String Pname){
            System.out.println("构造器被调用!完成对象属性的初始化~");
            age = Page;
            name = Pname;
        }
        public Constructor(int Aage){
            age = Aage;
        }
        public Constructor(String Aname){
            name = Aname;
        }
        public Constructor(){
            System.out.println("因为系统默认的无参构造器被覆盖,这里显式的创建无参构造器");
        }
    }
    
  • Java创建对象流程(详细):

    1. 在方法区先加载类信息(属性和方法信息,且只会加载一次)
    2. 在堆中分配空间,进行默认初始化
    3. 把地址赋给对象
    4. 进行指定初始化,覆盖原来的初始化值;这里先默认初始化(如int类型为0,String类型为null);若有显式初始化,再执行显式初始化(如定义类时直接初始化int为某个具体数值,则将默认初始化改为对应的初始化 );最后再执行构造器的初始化(如定义了构造器,创建对象后先执行前面两次初始化,最后根据个构造器的初始化进行对应改变)
    5. 把对象在堆中的地址返回给栈中的对象的引用(对象名)
    6. 注意:对象的地址在堆中。对象的引用(对象名)在栈中,指向堆中对应的对象地址
  • this关键字:

    • java虚拟机会给每个对象分配this,代表当前对象。哪个对象调用,this就代表哪个对象
    • this可以用来访问本类的属性、方法、构造器
    • this用来区分当前类的属性和局部变量
    • 访问成员方法的语法:this.方法名(参数列表);
    • 访问构造器语法:this(参数列表) 注:只能在构造器中第一个语句调用其他构造器
    • this只能在类定义的方法中使用,不能在类定义的外部中使用
    ThisKeyWord tkw = new ThisKeyWord(100, "this关键字");
    System.out.println("num:"+tkw.num+"\ttype:"+tkw.type+"\t对象地址的哈希码值:"+ tkw.hashCode());
    ThisKeyWord tkw1 = new ThisKeyWord(20);
    System.out.println("num:"+tkw1.num+"\ttype:"+tkw1.type+"\t对象地址的哈希码值:"+ tkw1.hashCode());
    tkw1.function2();
    tkw.function3();
    class ThisKeyWord{
        int num;
        String type;
        //如果我们的构造器的形参能直接写成属性名,就更加方便明了,但是出现一个问题,就是变量的作用域原则
        //下面的构造器形参的num、type是局部变量,而不是属性,导致使用下面构造器时,与本类的属性无关
    //    public ThisKeyWord(int num,String type){
    //        num = num;
    //        type = type;
    //    }
        //用关键词this关键字解决
        public ThisKeyWord(int num ,String type){
            //  this.  就是指向当前对象的属性
            this.num = num;
            this.type = type;
            System.out.println("this地址的哈希码值:"+this.hashCode());
        }
        public ThisKeyWord(int num){
            this(19,"Tom");//这句要放在首句中
            this.num = num;
    
        }
        public void function1(){
            System.out.println("方法一!");
        }
        public void function2(){
            System.out.println("使用this调用同类方法:");
            //第一种方式:直接调用
            function1();
            //第二种方式:用this调用,区别后面继承时候讲解
            this.function1();
    
        }
        public void function3(){
            String type = "A";
            int num = 10;
            System.out.println("type="+ type + "\tnum=" + num );
            System.out.println("this.type= " + this.type + "\tthis.num="+ this.num);
        }
    }
    
  • 练习:创建一个PersonDemo类,含有一个compareTo()方法来比较两个对象属性是否相同,返回true or false;

    PersonDemo p1 = new PersonDemo(18, "xiaoming");
    PersonDemo p2 = new PersonDemo(20,"xiaohong");
    boolean b = p1.compareTo(p2);
    System.out.println(b);
    class PersonDemo{
        int age;
        String name;
        public PersonDemo(){}
        public PersonDemo(int age,String name){
            this.age = age;
            this.name = name;
        }
        public Boolean compareTo(PersonDemo p){
            if(this.name.equals(p.name)&&this.age==p.age){
                return true;
            }
            else
                return false;
        }
    }
    

10.6 课后练习

  1. 编写类A01,定义方法max,实现求某个double 数组最大值,并返回

    double[] arr = {1.15,3.2,8,6};
    A01 a01 = new A01();
    Double res = a01.max(arr);
    if(res!=null){
        System.out.println("最大值为:"+res);
    }else{
        System.out.println("输入数组为空!");
    }
    
    class A01{
        public Double max(double[] arr){
            if(arr.length>0){
                double max = arr[0];
                for (int i = 1; i < arr.length ; i++) {
                    if(arr[i]>max){
                        max = arr[i];
                    }
                }
                return max;
            }
            else{
                return null;
            }
        }
    }
    
  2. 编写类A02,定义方法find ,实现某字符串数组中的元素查找,并返回索引,若找不到,返回-1

    String[] srr={"Lucy","Ben","Jack","Tom"};
    String tar = "Tom";
    A02 a02 = new A02();
    Integer res2 = a02.find(tar,srr);
    if(res2!=null) {
        System.out.println(res2);
    }else{
        System.out.println("输入数组为空!");
    }
    
    class A02{
        public Integer find(String tar, String[] srr) {
            if (srr.length > 0) {
                for (int i = 0; i < srr.length; i++) {
                    if (tar.equals(srr[i])) {
                        return i;
                    }
                }      
                return -1;
            }
            return null;
        }
    }
    
  3. 编写类Book,定义方法updatePrice,实现更改某本书的价格,如:若价格大于150,则更改为150,若价格大于100,则更改为100,其他不变。

    A03 a03 = new A03(99);
    System.out.println("书原价为:"+a03.price);
    double res3 = a03.updatePrice();
    System.out.println("更改后的价格为:"+res3);
    class A03{
        double price;
        public A03(double price){
            this.price = price;
        }
        public double updatePrice(){
            if(this.price>150){
                this.price = 150;
                return this.price;
            }else if(this.price<150&&this.price>100){
                this.price = 100;
                return this.price;
            }
            else{
                return this.price;
            }
        }
    }
    
  4. 编写类A04,定义方法copyArr实现数组的复制功能,输入旧数组,返回新数组,元素和旧数组一样

    A04 a04 = new A04();
    Double[] a04Arr = {1.2,5.5,7.3,4.2};
    Double[] res4 = a04.copyArr(a04Arr);
    if(res4!=null){
        System.out.println("原数组哈希码值:"+ a04Arr.hashCode());
        System.out.println("复制后哈希码值:"+ res4.hashCode()+"\n复制后元素为:");
        for (int i = 0; i < res4.length; i++) {
            System.out.print(res4[i] + " ");
        }
    }else{
        System.out.println("输入数组为空!");
    }
    class A04{
        public Double[] copyArr(Double[] arr){
            if(arr.length>0){
                Double[] copyarr = new Double[arr.length];
                for (int i = 0; i < arr.length; i++) {
                    copyarr[i] = arr[i];
                }
                return copyarr;
            }else{
                return null;
            }
        }
    }
    
  5. 编写类A05,定义属性半径,分别提供计算圆周长,圆面积的方法

    A05 a05 = new A05(1);
    System.out.println("周长为:"+a05.Perimeter());
    System.out.println("面积为:"+a05.Area());
    class A05{
        double r;
        public A05(double r){
            this.r = r;
        }
        public double Perimeter(){
            double perimeter= 2*Math.PI*this.r;
            return perimeter;
        }
        public double Area(){
            double area = Math.PI*r*r;
            return area;
        }
    }
    
  6. 编写类A06,定义两个属性表示操作数,定义四个方法完成加减乘除操作,并创建两个对象,分别测试。

    A06 a06 = new A06(1,0);
    System.out.println("和为:"+ a06.Add());
    System.out.println("差为:"+ a06.subtraction());
    System.out.println("积为:"+ a06.multiplication());
    if(a06.y!=0){
        System.out.println("商为:"+ a06.division());
    }else{
        System.out.println("被除数为0!");
    }
    class A06{
        double x;
        double y;
        public A06(double x,double y){
            this.x = x;
            this.y = y;
        }
        public double Add(){
            return this.x + this.y;
        }
        public double subtraction(){
            return this.x - this.y;
        }
        public double multiplication(){
            return this.x * this.y;
        }
        public Double division(){
            if(this.y!=0){
                return this.x/this.y;
            }else{
                return null;
            }
    
        }
     }
    
  7. 编写类A07,属性有名字,颜色,年龄;定义方法show()输出其信息。

    A07 a07 = new A07("旺财", "黄色", 12);
    a07.show();
    
    class A07{
        String name;
        String color;
        int age;
        public A07(String name,String color,int age){
            this.name = name;
            this.color = color;
            this.age = age;
        }
        public void show(){
            System.out.println("名字为:"+ this.name);
            System.out.println("颜色为:"+ this.color);
            System.out.println("年龄为:"+ this.age);
        }
    }
    
  8. 编写类A08,属性有(姓名、年龄、性别、工作、薪水),要求提供三个构造器可以分别初始化(姓名、年龄、性别、工作、薪水)(姓名、年龄、性别、)(姓名),要求充分福永构造器

    class A08{
        String name;
        int age;
        char sex;
        String job;
        double sar;
        public A08(String name){
            this.name = name;
        }
        public A08(String name,int age,char sex){
            this(name);
            this.age = age;
            this.sex = sex;
        }
        public A08(String name,int age,char sex,String job,double sar){
            this(name, age, sex);
            this.job = job;
            this.sar = sar;
        }
    }
    
  9. 定义一个Circle类,包含一个double的radius属性代表半径,findArea()方法返回圆的面积

    定义PassObject,在类中定义方法printAreas(Circle c,double times)

    在printAreas方法打印1到times之间每个整数的半径值和对应的面积

    在main中调用printAreas(),输出

    Circle circle = new Circle();
    PassObject passObject = new PassObject();
    double times = 5.5;
    passObject.printAreas(circle,times);
    class Circle{
        double radius;
        public Circle(){}
        public Circle(double radius){
            this.radius = radius;
        }
        public double findArea(){
            return Math.PI*radius*radius;
        }
        public void setRadius(double radius){
            this.radius = radius;
        }
    }
    class PassObject{
        public void printAreas(Circle c,double times){
            System.out.println("Radius\tArea");
            for (int i = 1; i <= times; i++) {
                //c.radius = i;     这里可以直接用c.radius来赋值改变属性,因为radius是public的,可以直接访问,当不是public时,需要用get/set来改变
                c.setRadius(i);
                System.out.println(i+"\t\t"+c.findArea());
            }
    
        }
    }
    
    
  10. 设计一种类,可以和电脑猜拳,并显示结果清单。0表示石头;1表示见到;2表示布

    FingerGuess fingerGuess = new FingerGuess();
    Play play = new Play();
    int time = 5;
    play.play(fingerGuess,5);
    class FingerGuess{
        int finger;
        int comGuessNum;
    
        public void setFinger(int finger){
            this.finger = finger;
        }
        public int computerNum(){
            Random random = new Random();
            comGuessNum = random.nextInt(3);
            return comGuessNum;
        }
    }
    class Play{
        public void play(FingerGuess f,int times){
            Scanner scanner = new Scanner(System.in);
            System.out.println("电脑\t 我\t 结果");
            for (int i = 1; i <= times; i++) {
                System.out.println("请输入你要出的0~2:");
                int userNum = scanner.nextInt();
                if(userNum==0||userNum==1||userNum==2){
                    f.setFinger(userNum);
                    f.computerNum();
                    int comGuessNum = f.computerNum();
                    System.out.println("我的出拳:"+ userNum + "电脑出拳:"+comGuessNum);
                    if(userNum==comGuessNum){
                        System.out.println("平局");
                        continue;
                    }
                    else if((userNum==0&&comGuessNum==1)||(userNum==1&&comGuessNum==2)||(userNum==2&&comGuessNum==0)){
                        System.out.println("用户赢!");
                        continue;
                    }else{
                        System.out.println("电脑获胜!");
                        continue;
                    }
                }else{
                    System.out.println("输入有误");
                    break;
                }
            }
        }
    }
    
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值