Java学习 day06_面向对象02

本文详细介绍了Java中的值传递和引用传递概念,通过示例展示了如何交换两个对象的属性和引用。同时,深入探讨了静态成员的特性,包括它们在内存中的位置、创建时间和调用方式。文章还提到了静态成员变量和方法的使用场景,以及静态方法不能访问非静态成员的原因。最后,给出了几个涉及静态成员和方法的编程练习。
摘要由CSDN通过智能技术生成

思考

定义一个学生类,然后创建两个学生对象,再创建一个方法,参数为两个学生对象的引用,用于交换两个学生对象的引用,原学生对象的引用会被交换吗?

package com.cskaoyan.javase._7callby;

/**
 * @description: 值传递
 * @author: wuguidong@cskaoyan.onaliyun.com
 **/

/**
 * > 值传递和引用传递的练习
 * >
 * > 写代码然后,回答问题
 *
 * - 定义一个学生类,该类具有一个int属性age
 *   - 在测试类中写一个方法,交换两个Student对象的age属性
 *   - 请问能够交换成功吗?原因是什么?
 *
 * - 在测试类中写一个方法,传入两个int类型参数
 *   - 然后交换两个参数的值
 *   - 请问能够交换成功吗?原因是什么?
 *
 */
public class Demo {
    public static void main(String[] args) {
        //创建对象,调用swap方法
        Student s1 = new Student(18);
        Student s2 = new Student(81);
        //方法可以改变对象的状态,就是说方法能够改变对象中成员变量的取值
        swapAge(s1, s2);
        System.out.println(s1.age); //81
        System.out.println(s2.age); //18

        int a = 10;
        int b = 100;
        swapInt(a, b);
        //方法无法改变基本数据类型的实参的取值,因为Java是值传递,得到的是实参的拷贝(副本)
        System.out.println(a);
        System.out.println(b);


        Student s3 = new Student();
        Student s4 = new Student();
        System.out.println("交换之前:");
        System.out.println(s3);
        System.out.println(s4);
        //方法得到的是引用的拷贝,再方法当中交换了两个拷贝引用指向的对象,对原先的实参没有影响
        //方法不能改变引用指向的对象
        //一个方法是不能改变另一个方法的局部变量的
        swapStudent(s3, s4);
        System.out.println("交换之后:");
        System.out.println(s3);
        System.out.println(s4);
    }

    //交换两个学生对象的age属性
    public static void swapAge(Student s1, Student s2) {
        int temp;
        temp = s1.age;
        s1.age = s2.age;
        s2.age = temp;
    }

    public static void swapInt(int a, int b) {
        int temp;
        temp = a;
        a = b;
        b = temp;
    }

    //交换两个学生的引用(地址)
    public static void swapStudent(Student s1, Student s2) {
        Student temp;
        temp = s1;
        s1 = s2;
        s2 = temp;
        System.out.println("交换方法当中:");
        System.out.println(s1);
        System.out.println(s2);
    }
}

class Student {
    int age;

    public Student() {
    }

    public Student(int age) {
        this.age = age;
    }
}

包装类的概念

在Java当中,万物皆对象,但是显然基本数据类型不是对象,所以Java就提供了四类八种基本数据类型的包装类,让基本数据类型也成为对象
Java当中存在自动装卸箱的功能 <—> 基本数据类型和它的包装类可以无缝转换,可以直接用等号连接
剩下的包装类,所有的包装类都在java.lang

  • byte --> Byte
  • short —> Short
  • int —> Integer
  • long —> Long
  • float —> Float
  • double —> Double
  • boolean —> Boolean
  • char ----> Character
public class Demo {
   static int end = Integer.MAX_VALUE;
   static int start = end - 5;

   public static void main(String[] args) {
       /*int count = 0; //计数器
       for (int i = start; i < end; i++){
           count++;
           //System.out.println(count);
       }
       System.out.println(count);*/

       System.out.println(Byte.MAX_VALUE); //127
       System.out.println(Byte.MAX_VALUE + 1); //128
       System.out.println(((byte) (Byte.MAX_VALUE + 1)));

       System.out.println(Short.MAX_VALUE);
       System.out.println(Short.MAX_VALUE + 1);
       System.out.println(((short) (Short.MAX_VALUE + 1)));

       System.out.println(Long.MAX_VALUE);
       //Numeric overflow in expression
       System.out.println(Long.MAX_VALUE + 1);

       System.out.println(Character.MAX_VALUE + 0);
       System.out.println(Character.MAX_VALUE + 1);
       System.out.println(((char) (Character.MAX_VALUE + 1) + 0));
       System.out.println();

   }

   public static void test() {
       Integer i = 100;
   }
}


static 关键字

static关键字是我们从”hello world“开始,就频繁使用的一个关 键字

例如我们的main方法,例如我们一直在写的static方法

  • 但是我们经常用,却不知道为什么这么用
  • 那么今天就正式学习static关键字

  • 引例
练习:
	创建一个学生类,用来描述我们班全体同学,要求
		属性:姓名,性别,年龄,学号,学校信息
		行为:吃饭,学习

对于一个班级的同学来说,学校名字都是固定的

我们虽然知道这一点,但是我们还是要,不厌其烦的在每次创建对象的时候,给schoolName赋值

这显然是没有必要的,浪费时间的同时也浪费内存空间

  • 怎么改进这个设计?

    • 可以把成员变量schoolName中直接初始化赋值, 但是每个对象中都存有一个成员变量,浪费空间
    • 最重要的,我们在设计类的时候其实已经知道这个属性,是全体对象都有的
      • 我们希望这个属性不是属于某一个对象的,而是一种类的特征,是属于类的,属于全体对象的
  • 上面这个改进不是我们最希望的样子

    • 那么能不能在内存中找一块区域,每个对象都共用这片区域,把schoolName放进去?
    • 这样做既节省时间,又节省空间
    • 最重要的体现了该属性属于类——只要是这个类的对象都该有这个属性

在JVM内存的设计当中,确实在方法区中开辟了一块单独的空间

用于存放这些 “所有对象共享,整个类的对象都只有一份的数据”

在Java当中把存放在这个区域的成员,称之为静态成员,包括静态成员变量和静态成员方法

语法:
	1,成员变量在数据类型前加static,静态成员变量或者简称静态变量
	2,成员方法在返回值类型前加static,是为静态成员方法或者简称静态方法
	静态成员变量: 指的是用static修饰的普通成员变量
                也就是说把成员变量前面加一个static修饰就成为了静态成员变量
   静态成员方法: 指的是用static修饰的普通成员方法
       也就说把成员方法的访问权限修饰符后面加上一个static就是静态成员方法
       
  因为静态成员变量是全体对象共享的,所以只要某个地方修改了,那么对于全体对象而言都会跟着改变
  • 修改一下schoolName为static修饰,验证一下该成员是否成为所有对象共享
public class NewDemo {
    public static void main(String[] args) {
        //WangDaoStudent s1 = new WangDaoStudent("张三", "男");
        //WangDaoStudent s2 = new WangDaoStudent("李四", "女");
        //Static member 'schoolName' accessed via instance reference
        //静态成员schoolName被访问通过一个实例(对象)的引用
        //告诉你,不应该用一个对象去访问静态成员
        //这种全体对象所共享的成员,不应该用某个对象去调用,因为它不属于你这个对象
        //它是属于类,所以它应该用类名去访问(调用)
        //System.out.println(s1.schoolName);
        //s1.schoolName = "C++训练营";
        //System.out.println(s2.schoolName);

        //正确的访问方式:
        System.out.println(WangDaoStudent.schoolName);
        WangDaoStudent.schoolName = "C++";

        //这里请大家思考一个问题: 这里访问静态成员,需要对象吗? :  访问静态成员,没有对象就可以访问
    }
}

class WangDaoStudent {
    //成员变量
    String name;
    String gender; 
    int age;
    int stuId;

    //定义静态成员变量
    static String schoolName = "王道训练营";

    //成员方法
    public void eat() {
        System.out.println("除了学习,就是吃饭!");
    }

    public void study() {
        System.out.println("除了吃饭,就是学习!");
    }

    //用构造方法来给成员变量赋值
    public WangDaoStudent(String name, String gender) {
        this.name = name;
        this.gender = gender;
    }
}

  • 很容易就可以验证出这一结果,那么接下来,我们结合我们的内存图去深入的学习static关键字
    在这里插入图片描述
    在这里插入图片描述

再次总结触发类加载的原因
1. 创建一个对象
2. 调用一个类当中的main方法
3. 使用一个类当中的静态成员变量或静态成员方法

总结static

  • static修饰成员,称之为静态成员,包括静态成员变量和静态成员方法

  • 随着类加载完毕,静态成员就存在,并且能够使用了

    • 静态成员变量在类加载过程中完成初始化,并且具有默认值
    • 静态成员方法的二进制指令集合在类加载过程也准备完毕,可以调用了
  • 静态成员领先于对象存在,不依赖于对象而存在

    • 静态成员变量在创建对象之前就已经创建了
      • 类加载的时候创建,类加载只有一次,所以静态成员也只有一份且被类所有对象共享
    • 静态方法被所有类对象共享,且无需创建对象就能调用
  • 仍然可以创建对象去调用静态成员,但是规范的Java代码只建议通过类名直接访问

    • 静态成员变量:类名.变量名
    • 静态成员方法:类名.方法名
    • 对象调用的方式,好像该成员属于对象,但显然静态成员属于全体对象,属于类
  • 在很多书籍博客中,由于静态成员的随着类加载而存在的特点

    • 静态成员是该类全体对象共有,属于类

    • 所以也称类成员,类属性,类变量,类方法

    • 知道这种称呼即可,但是大家还是习惯叫静态成员

    • C语言用static定义那些在方法外面也可以使用的局部变量

      C++把这个关键字拿来了 但是完全改变了它的含义

      Java又拿过来了

比较静态成员变量和普通成员变量

从以下四个角度

  • 所属不同
  • 在内存中的位置不同
  • 在内存中出现时间不同
  • 调用方式不同

成员方法的区别也类似

  • 所属不同
    • 静态成员变量属于类,所以也称为为类变量
    • (普通)成员变量属于对象,所以也称为对象变量(对象变量)
  • 在内存中的位置不同
    • 静态成员变量,在java8之前,静态成员存储在方法区当中,但是8之后移到了堆上
    • 成员变量存储于堆内存,每个对象独享自己的成员变量
  • 在内存中出现时间不同
    • 静态变量随着类的加载而加载,比成员变量出现的要早
    • 成员变量随着对象的创建而存在
  • 调用方式不同
    • 静态变量可以通过类名调用,也可以通过对象调用(不推荐/不合理的方式)
    • 成员变量只能通过对象名调用,必须创建对象

那么我们使用static的场景是什么?

  • static的使用场景
    • 静态成员变量:当存在需要所有对象共享的变量时,应该使用static
    • 静态成员方法:当不需要对象只是需要便捷的调方法时,使用static,广泛应用于工具类中,方便访问调用
      • 数组工具类

关于static两个简单的小问题

为什么静态成员方法中不能去访问普通成员变量?

  •    静态成员是在类加载结束后就能使用的,这个时候完全可能没有对象,
       所以static的静态方法不可能有this隐含传参,所以它不能访问普通成员变量
    

*** 在一个普通成员方法中,能不能访问静态成员变量?***

  •    普通成员方法调用时,一定有对象,既然有对象,一定类加载,所以可以在成员方法中访问静态成员(变量和方法)
    

A Little Trick

// 按照static方法的特点,Demo2能够使用Demo类中的static方法,直接用Demo.main(null)就可以直接调用,本质上就是调用一个类的静态方法,main方法需要传入一个String[],这里直接传入了一个null
// 执行Demo2输出的结果是 A和hello world
public class Demo2 {
    public static void main(String[] args) {
        //System.out.println(A.test(););
        A.test();

        Demo.main(null);
    }
}
class A{
    public static void test(){
        System.out.println('A');
    }
}

//-----------------------------------
public class Demo {
    int a;
    static int b;

    public void test() {
        //在能够用类名点访问时,尽量加上,提升代码的可读性
        System.out.println(Demo.b);
    }

    public static void main(String[] args) {
        //int[] arr = new int[1];
        //Arrays.toString(arr)
        //Non-static field 'a' cannot be referenced from a static context
        //System.out.println(a);
        System.out.println("hello world!");
    }
}

注意事项补充:

  • 一个类中,静态方法无法直接调用非静态的方法和属性,也不能使用this,super关键字
    • 经典错误:Non-static field/method xxx cannot be referenced from a static context
    • 原因:静态方法调用的时候,可能还没有对象,直接访问属于对象的成员变量和成员方法显然不合适
  • 反过来,一个非静态的方法,可以访问静态的成员
    • 因为有对象的时候,一定有静态成员
    • 建议采用这种访问形式的时候,使用类名.变量名的形式访问,以示区别,增加代码可读性
  • 只存在静态成员变量,不存在“静态局部变量”
    • 局部变量只有在调用的时候才有意义
    • 而静态变量在类加载时就初始化,就存在了
    • 如果我一直不调用这个方法,这个“静态局部变量”就一直占着空间,没有意义
  • 静态方法是类所有,那么静态方法的局部变量就也是类所有,为什么静态方法中也不能有静态局部变量?
    • 局部变量一定是方法所有
    • 静态方法也是方法,不调用其中的局部变量也没意义
  • 静态成员变量不建议用构造方法赋值
  • 普遍来说,访问静态成员,都建议加上类名去访问,提升代码可读性

一个小练习

public class Demo {
	//1.程序从main方法进入,但是对于一个类来说,执行静态方法可以没有对象,但一定要先进行类加载,在类加载的过程中,会去执行静态成员变量,在这里就是去new 一个cat
    static Cat cat = new Cat(); 
  // 8.这里创建Dog对象,直接执行构造函数
	Dog dog = new Dog();
	Dog dog2;

    public static void main(String[] args) {
    // 6.static修饰的东西执行完毕,类加载完成,可以开始执行静态main方法了
        System.out.println("hello world!");
        
    //7.创建Demo对象,给Demo的成员变量赋初始值会先于构造函数,所以又去new Dog了
    //9.最后绕回来,执行Demo的构造函数
        Demo d = new Demo();
    }

    public Demo() {
        System.out.println("demo");
    }
}

class Cat {
	
    static Dog dog = new Dog(); //static3 int a = 10;
    
	// 2.创建Cat对象,要先进行类加载,将static修饰的静态成员变量赋值,这就导致程序又去执行了new Dog
	
	//4.由于已经类加载过了,直接使用构造函数,所以输出了第一个cat,而后返回到刚才创建dog的时候

    //5.绕了一圈,终于可以开始执行最开始new Cat的那个构造函数了,然后返回到主函数
    public Cat() {
        System.out.println("cat");
    }
}

class Dog {
	
    static Cat cat = new Cat();
    // 3.new Dog的第一步仍然是类加载,所以执行new Cat
    // 5.类加载完成,执行构造器的内容,输出dog,然后返回到第一次new Cat的那一层
    public Dog() {
        System.out.println("dog");
    }
}

/* 执行结果如下:
	cat
	dog
	cat
	hello world!
	dog
	demo
*/


作业题(我的代码)

请给出方法method,并让程序输出结果是“a = 100 , b = 200”
这道题是值传递相关的练习题,大家可以先把这道题放一放,做完后面的再回头来思考
纯属思考题,脑筋急转弯题,实在想不出来百度或者参考老师代码

public static void main(String[] args) {
      int a = 10;
      int b = 20;
      method(a, b); //请自己写一个方法,输出“a = 100 , b = 200”
      System.out.println("a = " + a);
      System.out.println("b = " + b);
  }
package homework;

public class Exercise01 {
  public static void main(String[] args) {
      int a = 10;
      int b = 20;
      method(a, b);
      System.out.println("a = " + a);
      System.out.println("b = " + b);
  }
  public static void method(int a, int b){
      System.out.println("a = " + a*10);

      System.out.println("b = " + b*10);
      System.exit(0);
  }
}


写一个数组的工具类ArrayTool, 要求提供如下方法:
遍历,求最大值,最小值,逆置数组元素
查表(在数组中查找指定元素,若不存在,待查找元素返回-1,若存在返回元素在数组中首次出现的位置)
查表(在数组中查找指定元素,若不存在,待查找元素返回-1,若存在返回元素在数组中最后一次出现的位置)

package homework;

import java.util.Arrays;

public class Exercise02 {

    public static void main(String[] args) {
        int[] arr = new int[]{13, 45, 8, 9, 0, 77, 91, 88, 100, 34, 88, 21, 104, 44, 22, 88, 67};
        ArrayTool arrayTool = new ArrayTool(arr);
        System.out.print("遍历数组:");
        arrayTool.traverseArray();
        System.out.print("求最大值:");
        System.out.println(arrayTool.getMax());
        System.out.print("求最小值:");
        System.out.println(arrayTool.getMin());
        System.out.print("查找指定元素,并找出第一次出现的位置:");
        arrayTool.getFrontElement(88);
        System.out.print("查找指定元素,并找出最后出现的位置:");
        arrayTool.getBackElement(88);
        System.out.print("逆置数组:");
        arrayTool.reverseArray();
    }


}

class ArrayTool{
    int[] array;
    public ArrayTool(){

    }
    public ArrayTool(int[] array){
        this.array = array;
    }

    public void traverseArray(){
        if(array == null || array.length == 0){
            System.out.println("该数组为空!");
            return;
        }
        System.out.print("[");
        for (int i = 0; i < array.length; i++) {
            System.out.print(array[i] + " ");
        }
        System.out.println("\b]");
    }

    public int getMax(){
        if(array == null || array.length == 0){
            System.out.println("该数组为空!无法进行查找");
            return -1;
        }
        int tempMax = array[0];
        for (int i = 1; i < array.length; i++) {
            if(array[i] > tempMax){
                tempMax = array[i];
            }
        }
        return tempMax;
    }

    public int getMin(){
        if(array == null || array.length == 0){
            System.out.println("该数组为空!无法进行查找");
            return -1;
        }
        int tempMin = array[0];
        for (int i = 1; i < array.length; i++) {
            if(array[i] < tempMin){
                tempMin = array[i];
            }
        }
        return tempMin;
    }

    public void getFrontElement(int target){
        if(array == null || array.length == 0){
            System.out.println("该数组为空!无法进行查找");
            return ;
        }
        int targetIndex = -1;
        for (int i = 0; i < array.length; i++) {
            if(array[i] == target){
                targetIndex = i;
                break;
            }
        }
        System.out.println(target + "第一次出现的位置是" + targetIndex + "号位");
    }

    public void getBackElement(int target){
        if(array == null || array.length == 0){
            System.out.println("该数组为空!无法进行查找");
            return;
        }
        int targetIndex = -1;
        for (int i = array.length-1; i>=0; i--) {
            if(array[i] == target){
                targetIndex = i;
                break;
            }
        }
        System.out.println(target + "最后一次出现的位置是" + targetIndex + "号位");
    }

    public void reverseArray(){
        if(array == null || array.length == 0){
            System.out.println("该数组为空!");
            return;
        }

        int temp;
        for (int i = 0; i < array.length/2; i++) {
            temp = array[i];
            array[i] = array[array.length - 1 - i];
            array[array.length - 1 - i] = temp;
        }
        System.out.println("逆序后的数组为:" + Arrays.toString(array));

    }

}


先生成一个随机数(1~100的整数),再键盘输入猜测的数
如果猜的数大了或者小了,给出提示,继续猜,直到猜中为止

package homework;

import java.util.Random;
import java.util.Scanner;

public class Exercise03 {
    public static void main(String[] args) {
        Random random = new Random();
        int randonNum = random.nextInt(100) + 1;
        boolean flag = true;
        Scanner sc = new Scanner(System.in);
        int count = 0;
        while(flag){
            System.out.print("请输入您猜的数:");
            int guessNumber = sc.nextInt();
            count++;
            if(guessNumber > randonNum){
                System.out.println("您输入的数大了,请重新输入!");
            }else if(guessNumber < randonNum){
                System.out.println("您输入的数小了,请重新输入!");
            }else {
                System.out.println("您猜对了!您一共猜了" + count + "次");
                break;
            }
        }
    }
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值