head first java学习-第十章

173 阅读7分钟

第十章-数字与静态

静态方法

Math这个类中的所有方法都是静态的(Java中没有东西是全局的,静态是最接近全局的),所以程序员无需Math的实例,只需使用它的类本身。如果硬要创建Math的实例,编译器会报错(因为Math的构造函数被标记为私有private,这代表无法从外部调用构造函数,也就无法新建Math对象)。

所以如果类只有静态的方法(比如Math类),你可以将构造函数标记为私有private的,以避免被初始化。

所谓静态方法指的是方法不需要类的实例(也就不需要对象)。但静态方法可以存取静态变量。

使用static关键词标记静态方法。

//静态方法
public static int min(int a,int b){
    //返回a与b中较小的值的程序代码
}

//调用Math类的方法,直接用类名调用静态的方法
Math.min(42,36);
//用引用变量(引用到类的对象)的名称调用非静态的方法

第八章中讨论到的抽象类通过使用abstract修饰字来标记类以避免被初始化。也可以用私有的构造函数来限制非抽象类被初始化(Math类就是这样)。

不过并非有一个或多个静态方法的类就不能被初始化,事实上,只要有main()的类就算有静态的方法。一般,程序员会写出mian()来启动或测试其他的类,从main()中创建类的实例并调用新实例上的方法。因此,程序员可以在类中任意组合静态与非静态的方法。但是要注意,任何非静态的方法都必须以相应的实例(类的对象)来操作,静态方法以类名来操作。

取得新对象的方法只有通过new或者序列化(deserialization)以及Java Reflection API。

静态的方法也不能调用非静态的方法(因为非静态方法有可能使用实例变量。不过即使该非静态方法没有调用实例变量也不能被静态方法调用)。 image.png

静态变量

静态变量的值对所有的实例来说都相同,是被同类的所有实例共享的变量。静态变量通过类名来存取。

比如要计算Duck类有多少实例已经被创建出来。

public class Duck {
    private int size;
    //静态变量只会在类第一次载入的时候被初始化
    private static int duckCount=0;
    
    //每当构造函数执行的时候,此变量的值就会被递增
    public Duck() {
        duckCount++;
    }
}

Duck对象不会维护自己的一份duckCount拷贝。实例变量是每个实例一个,静态变量是每个类一个。

静态变量是在类被加载时初始化的(通常,Java虚拟机会加载某个类是因为第一次有人尝试要创建该类的实例,或是使用该类的静态变量和方法),静态变量的初始化有两项保证:

  1. 静态变量会在该类的任何对象被创建之前就完成初始化;
  2. 静态变量会在该类的任何静态方法执行之前就初始化。

如果程序员没有给静态变量赋初值,那就会被设定为默认值。默认值和实例变量被赋予的默认值一样。

final关键字

静态的final变量是常数(除此之外没有别的方法可以标识变量为不变的常数)。 一个被标记为final的变量代表它一旦被初始化之后就不会变动。

常数变量的名称应该都要大写字母(Java中所谓常量就是静态final变量)。

静态final变量的初始化的两种方法:

//在声明类时初始化
public class Foo {
    public static final int FOO_X=25;
}
//在静态初始化程序中初始化
public class Bar {
    public static final double BAR_SIGN;
    //下面这段程序会在类被加载时执行
    static {
        BAR_SIGN=(double)Math.random();
    }
}

若静态final变量没有以这两种方式之一来初始化,那么编译器会报错,注意这和静态变量不一样。

final关键字的其他用处:

final不止用在静态变量上,也可以用final关键字来修饰非静态变量(包括实例变量,局部变量,或是方法参数)

  1. final变量代表我不能改变它的值;
  2. final的方法代表我不能覆盖掉该方法;
  3. final的类代表我不能继承该类(也就是不能创建它的子类)。
//非静态final变量
class Foof {
    //final实例变量
    final int size=3;
    final int whuffie;
    
    Foof() {
        //错误,whuffie已经不能改变
        whuffie=42;
    }
    //final参数
    void doStuff(final int x) {
        //不能改变x
    }
    //final局部变量
    void doMore() {
        final int z=7;
        //不能改变z
    }
}

//final方法
class Poof {
    final void calcWhuffie() {
        //该方法不能被覆盖
    }
}

//final类
final class MyMostPerfectClass {
    //不能被继承过
}

将类标记为final主要是为了安全,有的类不需要被继承,比如String。

如果类已经是final,那么不需要再将它的方法标记为final,因为一个类如果不能被继承,那么它的类根本就不能被覆盖。如果只想要类中的部分方法不能被覆盖,那么单独标记这些方法为final即可。

Math的方法:

//返回介于0.0-1.0之间的双精度浮点数(即double类型)
Math.random()

//返回双精度浮点数类型参数的绝对值
//该方法有覆盖版本,因此传入整型参数会返回整型,传入double类型参数会返回double类型
Math.abs()

//根据参数是浮点型或双精度浮点型返回四舍五入之后的整型或长整型值
//文字直接表示的浮点数都会被当做双精度浮点数,除非后面有加上f
Math.round()
int x=Math.round(-24.8f); //返回25
int y=Math.round(24.45f); //返回24

//min()有针对各种数字类型的覆盖版本
Math.min()

//max()同样多个版本
Math.max()

在Java 5.0之前primitive主数据类型和对象引用完全没有能交换使用的方法。当程序员需要以对象方式来处理primitive 主数据类型时,就把它包装起来。

比如我没办法把primitive主数据类型放进ArrayList或HashMap中。这种情况下,每一个primitive主数据类型都有一用来包装的类,这些包装类都在java.lang这个包中。所以程序员不用去import它们。

Boolean类   -boolean类型
Character类 -char类型
Byte类      -byte类型
Short类     -short类型
Interger类  -int类型
Long类      -long类型
Float类     -float类型
Double类    -double类型

//包装值
int i=25;
//传入primitive主数据类型给包装类的构造函数
Integer iWrap=new Integer(i);

//解开包装
int unWrapped=iWrap.intValue();

为了解决这个问题,引入autoboxing功能,该功能可以自动将primitive主数据类型转换为包装过的对象。 image.png

图中的generic类型是什么类型????

image.png image.png

String值转换为primitive主数据类型的值(包装的一组实用的静态方法)

String s="2";
int x=Integer,parseInt(s);
double d=Double.parseDouble("420.24");
boolean b=new Bollean("true").booleanValue();

将primitive主数据类型转换为String

double d=45.56;
//通过将数字接上现有的String转换为String类型
String s=""+d;
//+运算符是Java中唯一重载过的运算符

//Double类的静态方法
String s=Double.toString(d);

数字的格式化

使用静态方法String.format()

String s=String.format("%, d", 1000000000);
System.out.println(s);
//输出1,000,000,000

String s=String.format("I have %,.2f bugs to fix.", 476578.09876);
//输出I have 476,578.10 bugs to fix.

image.png 具体的数字格式化内容先省略

日期时间格式化:

//使用Date需要下面的import
import java.util.Date;

//完整的日期和时间
String.format("%tc", new Date());
//输出Sun Nov 28 14:52.41 MST 2004

//只有时间
String.format("%tr", new Date());
//输出03:01:47 PM

//周、月、日
Date today=new Date();
String.format("%tA, %tB, %td", today,today,today);
//输出Sunday, November 28
//<符号表示重复利用之前用过的参数
String.format("%tA, %<tB, %<td", today);

使用java.util.Calendar来操作日期 Calendar是个抽象的类,所以只能用子类去继承该类,而不能声明该类的实例。

import java.util.Calendar;

//因为Calendar是抽象类,所以下面代码错误
Calendar cal=new Calendar();

//getInstance是静态方法,
//静态方法在类上,而不在某个特定的实例上
//可以通过调用getInstance方法,返回具体子类的实例(继承了Calendar)
//大部分版本的Java都会返回一个叫Gregorian-Calendar的实例
Calendar cal=Calendar.getInstance();

image.png

image.png

静态import

image.png