第十章 数字与静态
1. 静态方法
之前我们用到的方法大部分都需要通过变量及对象来实现。但是静态方法是一种不需实例变量及对象行为也能够实现的方法。static这个关键词可以标记出不需要类变量的方法。
public static int main(int a, int b){
} //返回a与b中的较小的值
带有静态变量方法的意义: 这些类通常不打算被初始化(不一定)。我们可以用抽象类abstract这个修饰词来标记类让其不能被创建出实例,或者用private的构造函数和方法来加以限制。
(书看得有点云里雾里,日后补充)
2. 静态变量
静态变量是被同类的所有实例共享的变量
。一般在前面加上static。
public class Duck{
private int size;
private static int duckCount = 0; //静态变量duckCount
public Duck(){
duckCount++;
}
}
此静态变量只会在第一次载入的时候被初始化,所有Duck的实例只有一份静态变量duckCount。
与实例变量的区别:实例变量是每个实例中一个,静态变量是每个类中一个。
静态变量的起始动作
- 静态变量会在该类的任何对象创建之前就完成初始化
- 静态变量会在该类的任何静态方法执行之前就初始化
class Player {
static int playerCount;
private String name;
public Player(String n) {
name = n;
playCount++;
}
}
public class PlayerTestDrive {
public static void main(String[] args){
System.out.println(Player.playCount);
Player one = new Player("Lebron James");
System.out.println(Player.playCount);
}
}
输出结果为:
>>> 0
>>> 1
静态变量playCount在加载类Player时就会被初始化,所以在PlayerTestDrive中对象创建之前就有默认值0,所以第一行输出会等于0。在生成对象"Lebron James"之后,构造函数中playerCount累加1,所以最后输出变为1。
(PS:long,short等primitive主数据类型整数的默认值为0,primitive主数据类型的浮点数默认值为0.0,Boolean是false,对象引用是null)
3. final
用final作为修饰词后大概就表示被修饰后的状态变为不可更改的最终形态。
3.1 静态final变量
Mathp.PI的初始化过程如下
public static final double PI = 3.141592653589793;
此变量被标记为public,因此可供各方读取。
此变量被标记为static,所以不需要Math的实例。
此变量被标记为final,所以圆周率是不变的。
NOTE: 常数变量的名称应该都为大写字母
静态final变量的初始化
- 声明的时候
public class foo {
public static final int XO = 20; //XO要大写
}
- 在静态初始化程序中
public class Foo {
public static final int XO;
static { //这段程序会在类被加载时执行
XO = 20;
}
}
3.2 非静态final
- final的变量代表你不能改变它的值
- final的方法代表你不能覆盖掉该method
- final的类代表你不能继承该类
class Foof {
final int size = 3; //size无法改变
final int whuffie;
Foof() {
whuffie = 40; //whuffie无法改变
}
void doStuff(final int x) {...} // x无法改变
void doMore() {
final int z = 7; //z无法改变
}
}
class Poof {
final void calcWhuffie(){ //该方法不能被覆盖
...
}
}
final class MyMostPerfectClass { //该类不能被继承
...
}
静态与final要点
- 静态的方法应该用类的名称来调用,而不是用对象引用变量
- 静态的方法可以直接调用而不需要堆上的实例
- 静态的方法是一个非常实用的方法,它不需要特别的实例变量值
- 静态的方法不能存取非静态的方法
- 如果类只有静态的方法,你可以将构造函数标记为private的以避免被初始化
- 静态变量为该变量所属类的成员共享。静态变量只会有一份,而不是每个实例都有自己的一份
- 静态方法可以存取静态变量
- 在Java中的常量是把变量标记为static和final的
- final的静态变量值必须在声明或静态初始化程序中赋值
- 常量的命名惯例是全部使用大写字母
- final值一旦被赋值就不能更改
- final的方法不能被覆盖
- final的类不能被继承
4. autoboxing
autoboxing功能能够自动地将primitive主数据类型转换成包装过的对象。在java 5.0以前的版本都是没有这个功能的,那个时候如果想往ArrayList中加入一个integer,需要先对其进行包装放入ArrayList,在取出值时有需要解开包装。
autoboxing的用处
1. 方法的参数
如果参数是某种包装类型,你可以传入相对应的primitive主数据类型,反之亦然
void takeNumber(Integer i) { }
2. 返回值
如果method声明为返回某种primitive主数据类型,你也可以返回兼容的primitive主数据类型或该primitive主数据类型的包装类型
int giveNumber() {
return x;
}
3. boolean表达式
任何预期Boolean值的位置都可以用求出Boolean的表达式来代替,例如说4>2或是Boolean包装类型的引用
if(bool) {
System.out.println("true");
}
4. 数值运算
你可以使用primitive主数据类型作为运算子的操作中以包装类型来替换。这代表你可以对integer的对象作递增运算
Integer i = new Integer(58);
i++;
Integer j = new Integer(5);
Integer k = j + 3;
5. 赋值
你可以将包装类型或primitive主数据类型赋给声明成相对应的包装或primitive主数据类型
Double d = x;
5. 包装类型的方法
- 将String转换成primit主数据类型
String s = "2";
int x = Integer.parseInt(s);
double d = Double.parseDouble("420.24");
boolean b = new Boolean("true").booleanValue(); //这个和别的有点不一样
- 将主数据类型值转换为String
double d =12.3;
String doubleString = "" + d;
double d = 12.3;
String doulbeString = Double.toString(d);
6. 数字格式化
基本的数字格式化由两个部分组成:
1. 格式指令
描述要输出的特殊格式
2. 要格式化的值
值,但注意不是所有值都能被格式化的
String s = String.format("%, d", 1000000000);
输出为:
>>> 1,000,000,000
格式化说明最多会有五个部分(不包括%符号)。下面的[ ]符号里面都是选择性的项目,因此只有%与type是必要的
。格式化说明的顺序是有规定的,必须要以这个顺序来指定。
几种常用类型有:
- %d – decimal(十进制)
- %f – floating point(浮点)
- %x – hexadecimal(十六进制)
- %c – character(字符)
7. 数字日期
我们可以调用java.util.Date来直接生成当前日期与时间,例如
//完整的日期与时间:%tc
String a = String.format("%tc", new Date());
//只有时间:%tr
String a = String.format("%tr", new Date());
//周,月,日: %tA %tB %td
Date today = new Date();
String a = String.format("%tA, %tB %td", today,today,today);
// or
String a = String.format("%tA, %<tB %<td", today);
输出分别为
>>> Wed Jun 26 15:53:14 CST 2019
>>> 15:53:14 PM
>>> Wednesday, June 26
Calendar的用法
需要调用java.util.Calendar来使用calendar,其可以实现许多操作日期时间的功能。
import java.util.Calendar;
Calendar c = Calendar.getInstance();
c.set(2019,5,26,15,55); //设定时间为当前时间,月份为0基,5代表六月
long day1 = c.getTimeInMillis(); //将目前时间转换为以millisecond表示
day1 += 1000*60*60;
c.setTimeInMillis(day1); //将c的时间加上一个小时
System.out.println("new hour " + c.get(c.HOUR_OF_DAY));
c.add(c.DATE, 35); //将日期加上35天,所以月份到了7月
System.out.println("add 35 days " + c.getTime());
c.roll(c.DATE, 35); // 滚动35天,只有日期会变,月份不会变
System.out.println("roll 35 days " + c.getTime());
c.set(c.DATE, 1); //直接设定DATE的值
System.out.println("set days to 1 " + c.getTime());
输出为:
>>> new hour 16
add 35 days Wed Jul 31 16:55:34 CST 2019
roll 35 days Thu Jul 04 16:55:34 CST 2019
set days to 1 Mon Jul 01 16:55:34 CST 2019
Calendar中的一些方法: