Java对象与类2.0

Posted 364.99°

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java对象与类2.0相关的知识,希望对你有一定的参考价值。


1.静态字段与静态方法

static修饰符

1.1.静态字段

对于非静态的实例字段,每个对象都有自己的一个副本。而对于一个静态(static)字段,每个类只有一个这样的字段(没有副本)。
静态变量属于类,故能直接用类来调用(类.静态字段),不属于任何单个的对象,即使没有实例化对象,copyright依然存在

public class LoveDeathRobots {
    private int number;
    private String name;
    private int duration;
    //将版权copyright设为静态变量
    static String copyright = "NetFlix";
    ……
}

1.2.静态常量

静态变量用得比较少,静态常量很常用
如Math类中定义的常量π
在程序中,可以用Math.PI来访问这个常量

注意: 由于每个类都可以修改公共字段,所以最好不要有公共字段(而公共常量/final字段却没问题)

public class Math{
   ……
   public static final double PI = 3.14159265358979323846;
   ……
}

1.3.静态方法

静态方法是不在对象上执行的方法
例如Math.abs(x)//计算数x的绝对值
    在完成运算时,它不使用任何Math对象(它没有隐式参数),可以认为静态方法是没有this参数的方法

注意: 1.可以使用对象调用静态方法
       2.满足两种情况可以使用静态方法:
          2.1.方法不需要访问对象状态,因为他需要的所有参数都通过显示参数提供
          2.2.方法只需要访问类的静态字段

1.4.工厂方法

工厂方法(factory method):如之前的LocalDate.now、LocalDate.of,是静态方法的另一种常见的用途。

NumberFormat类生成不同风格的格式化对象

import java.text.NumberFormat;

public class Test {
    public static void main(String[] args) {
        NumberFormat currencyFormatter = NumberFormat.getCurrencyInstance();
        NumberFormat percentFormatter = NumberFormat.getPercentInstance();
        double x = 0.99;
        System.out.println(currencyFormatter.format(x));
        System.out.println(percentFormatter.format(x));
    }
}

为什么不使用构造器?
    1.无法命名构造器。构造器名字必须和类名相同,但是这里希望有两个不同的名字,分别得到货币实例和百分比实例
    2.使用构造器时,无法改变所构造对象的类型。而工厂方法实际上将返回DecimalFormat类的对象

1.5.main方法

main方法是一个景泰坊

public static void main(String[ ] args),main方法不对任何对对象进行操作。在程序启动时还没有任何对象。静态的main方法将执行并构造程序所需要的对象

执行两个main函数

import java.time.LocalDate;
import java.util.Objects;

public class OnHoliday {
    public static void main(String[] args) {
        //声明一个对象数组
        HolidayLife []holiday = new HolidayLife[5];
        //设置假期起始日
        LocalDate date = LocalDate.of(2021,5,1);

        //设置对象数组的内容(利用带参的构造方法实例化5个对象)
        for (int i = 0;i < holiday.length;i++){
            holiday[i] = new HolidayLife(1,date.plusDays(i),(int)(2+Math.random()*5),(int)(1+Math.random()*5));
        }

        //输出假期内容
        String str = "%tF,第%d天假期,学习时长%d时,锻炼时长%d时%n";
        for (HolidayLife each : holiday){
            System.out.printf(str,each.getDate(),each.getDays(),each.getStudyTime(),each.getWorkoutTime());
        }

    }


}

class HolidayLife {
    //instance fields
    private int days;//private:访问修饰符,仅本类可以访问
    private LocalDate date;
    private int studyTime;
    private int workoutTime;

    //constructor
    public HolidayLife(int days){
        this.days = days;//this表示此对象
    }
    public HolidayLife(int days,LocalDate date,int studyTime,int workoutTime){
        Objects.requireNonNull(days,"The days can't be null");
        this.days = days;
        this.date = date;
        this.studyTime = studyTime;
        this.workoutTime = workoutTime;
    }

    //method
    public int getDays(){
        return days;
    }
    public LocalDate getDate(){
        return date;
    }
    public int getStudyTime(){
        return studyTime;
    }
    public int getWorkoutTime(){
        return workoutTime;
    }

    //main
    public static void main(String[] args) {
        HolidayLife holidayLife = new HolidayLife(5);
        System.out.printf("此次假期时长%d天",holidayLife.getDays());
    }
}

Object类关于处理null的方法
import java.util.Objects

关键字简介
static <T> void requireNonNull(T obj)
static <T> void requireNonNull(T obj,String message)
static <T> void requireNonNull(T obj,Supplier messageSupplier)
如果obj为null,就会抛出一个NullPointerException异常而没有消息或给定消息
static <T> T requireNonNullElse(T obj,T dafaultObj)
static <T> T requireNonNullElseGet(T obj,SUpplier<T> defaultSupplier)
如果obj不为null就返回obj,如果为null就返回默认对象

2.方法参数

Java程序设计语言总是采用按值调用,即方法得到的是参数值的一个副本(方法不能修改传递给它的任何参数变量的内容)

按值调用(call by value): 表示方法接收的是调用者提供的
按引用调用(call by reference):表示方法接收的是调用者提供的变量地址

方法可以修改按引用传递的变量的值,而不能修改按值传递的变量的值

2.1.分析

按值调用案例

public class CellByValue {
    private static int a = 3;
    public static void add(int x){
        x += x;
        System.out.println("进行运算后的值:" + x);
    }

    public static void main(String[] args) {
        CellByValue.add(a);
        System.out.println("进行运算后a的值:" + a);
    }
}


结果分析

x初始化为a的一个副本(值为3),x+=x后x取值为6,但是a的值仍是3,这个方法结束后,x不再使用

对象引用案例

public class CellByReference {
    public static void main(String[] args) {
        Role role = new Role("Tom",19);
        ageNow(role);
        System.out.println("19岁的Tom两年后的年龄:" + role.getAge());
    }

    private static void ageNow(Role r) {
        r.grow(2);
    }
}

class Role{
    private String name;
    private int age;
    //两个参数的构造器
    public Role(String name,int age){
        this.name = name;
        this.age = age;
    }
    //方法
    public void grow(int yaer){
        age += yaer;
    }
    public int getAge(){
        return age;
    }
}

结果分析

1.r初始化为role的一个副本(一个对象引用),grow方法应用于这个对象引用r,r和role同时引用的Role对象的年龄增加了2,方法结束后,参数r不再使用,对象变量role继续引用那个年龄增加到21的Role对象

可见,实现一个改变对象参数状态的方法是完全可以的,也十分常见,但并不是所有对对象采用的都是按引用调用

反例

//新增一个实现对象变量r1,r2交换对象引用的方法swap
    public static void swap(Role r1,Role r2){//实现对象变量r1,r2交换
        Role temp = r1;
        r1 = r2;
        r2 = temp
    }

当Java对对象采用的是按引用调用,name这个方法就可以实现交换

        Role role1 = new Role("Alen",20);
        Role role2 = new Role("Gelen",22);
        swap(role1,role2);

但,这个方法并没有改变存储在变量r1和r2中的对象引用

swap的参数r1和r2被初始化为两个对象引用的副本,这个方法交换的是这两个副本,在方法结束时,参数变量r1、r2都被丢弃了,变量role1与role2未做任何改变

2.2.小结

小结

Java程序设计语言对对象采用的不是按引用调用,实际上,对象引用是按值传递的

Java中对方法参数能做什么和不能做什么:
    方法不能修改基本数据类型的参数(数值型和布尔型)
    方法可以改变对象参数的状态
    方法不能让一个对象参数引用一个新的对象

3.对象构造

多种编写构造器的机制

3.1.重载

重载:多个方法,具有相同的名字,不同的参数

调用方法时,用各个方法首部中的参数类型特定方法调用中所使用的值类型进行匹配,来选出正确的方法。当编译器找不到匹配的参数,就会出现编译错误。

3.2.默认字段初始化

在构造其中没有显示地给字段设置初值,就会被自动的赋为默认值

数值为0,布尔值为false,对象引用为null

不明确地对字段进行初始化,会影响程序代码的可读性

3.3.无参数的构造器

很多类自带无参构造器,(没有编写构造器)由无参构造器创建对象时,对象状态会设置为适当的默认值

如果类中提供了至少一种构造器,但是没有提供无参构造器,那么构造对象时如果不提供参数就是不合法的

仅当类没有任何其他构造器的时候,才会得到一个默认的无参数构造器。当编写了其他构造器,还想调用无参构造器时,就必须编写一个无参构造器

3.4.调用另一个构造器

this:1.指示一个方法的隐式函数
      2.调用同一类的另一个构造器:构造器的第一句this(…)

    public Role(String name){
        this.name = name;
    }
    public Role(String name,int age){
        this(name);//调用第一个构造器
        this.age = age;
    }

3.5.初始化块

1.初始化数据字段

1.在构造器中设置值
2.在声明中赋值

2.初始化块

在一个类中,可以包含任意多个代码块,只要构造这个类的对象,这些块就会被执行

public class Count {
    private String name;
    private int count;
    private int addnum;
    
    //初始化块
    {
        count = addnum;
        addnum++;
    }
}

3.调用构造器的具体处理步骤

1.如果构造器第一行调用了另一个构造器,则基于所提供的参数执行第二个构造器
2.否则,
    2.1.所有数据字段初始化为其默认值(0,false,null)
    2.2.按照在类声明中出现的顺序,执行所有字段初始化方法和初始化块
3.执行构造器的主体代码

3.6.对象析构与finalize方法

Java会完成自动的垃圾回收,不需要人工回收内存,所以Java不支持析构器

显示的析构器方法:其中放置一些当对象不再使用时需要执行的清理代码

当某些对象使用了内存之外的其他资源,当资源不再需要时,就需要将其回收和再利用

1.如果一个资源一旦使用完就需要立即关闭,需要提供一个close方法来完成必要的清理工作
2.如果可以等到JVM退出,就可以用方法Runtime.addShutdownHook增加一个“关闭钩”(shutdown hook)。Java9可以使用Cleaner类注册一个动作,当对象不再可达时(仅清洁器还能访问这个对象),就会完成这个动作。


以上是关于Java对象与类2.0的主要内容,如果未能解决你的问题,请参考以下文章

Java——面向对象与类

对象与类

第三周学习《对象与类》心得

Java对象与类

java学习笔记(Core Java)4 对象与类

Java--对象与类