Head First Java学习:第十章-数字很重要
1、Math 方法:最接近全局的方法
一种方法的行为不依靠实例变量值,方法对参数执行操作,但是操作不受实例变量状态影响,那么为了执行该方法去堆上建立对象实例比较浪费。
举例:
Math mathObject = new Math();
报错:Math() has private access in java.lang.Math
而是可以直接使用:
int x = Math.round(43.4);
int y = Math.min(43,23);
int z = Math.abs(-34);
总结:在Math这个类中的所有方法都不需要实例变量值。因为这些方法都是静态的,所以无需Math的实例,你会用到的只有类本身。
2、非静态方法与静态方法的差别
当 需要方法而不需要类的实例,那么可以用 static 关键字标记出该。
使用 static 关键字标记“不需要类实例的方法”,称为“静态方法”。
举例:
public static int min(int a,int b){
//返回a与b中较小的值
}
如何调用:
- 以类的名称调用静态方法:min(88,43);
- 以引用变量的名称调用非静态的方法:
Song t = new Song();
t.play();
3、带有静态方法(static)的含义
抽象类(abstract修饰),不能创建出实例,即抽象的类不能被初始化。
非抽象的类如何限制初始化?
把构造方法标记为私有(private,只能被同一类的程序调用),则无法创建出类的实例,编译器会知道不能存取这些私有的构造函数。
不代表有一个或多个静态方法的类不能被初始化,只要有main的类都有静态方法。main()一般用来启动或测试其他类,从main() 中创建类的实例并调用新实例上的方法。
在类中可以组合静态方法和非静态方法。非静态方法都需要以某种实例操作,取得新对象的方法只有通过new或者序列化(deserialization)或者Java Reflection Api。
4、静态方法调用其他方法和变量
第一:静态方法不能调用非静态的变量
静态方法使用通过类的名称来调用,不需要类的实例(不知道堆上有哪些实例),所以也无法引用到该类的任何实例变量,也不会知道可以使用哪个实例变量值。
如果调用非静态的变量,会报错:non-static variable size cannot be referenced from a static context
第二:静态方法不能调用非静态的方法
非静态方法都是以实例变量的状态来影响该方法的行为。调用非静态方法,会在非静态方法中用到实例变量,但是静态方法无法引用该类的任何实例变量。
如果调用非静态的方法,会报错:non-static method getSize() cannot be referenced from a static context
5、静态变量
静态变量是共享的。
同一个类所有的实例共享一份静态变量。
实例变量:每个实例一个
静态变量:每个类一个,也叫类变量。类名.静态变量名 存取
代码举例:
package chap10;
public class Duck {
private int size;
// 静态变量只有在类第一次载入的时候被初始化
static int duckCount = 0;
// 构造函数执行时,静态变量duckCount的值就会增加1
public Duck(){
duckCount ++;
System.out.println("duckCount的值是:"+duckCount);
}
public void setSize(int s){
size = s;
}
public int getSize(){
return size;
}
}
package chap10;
public class TestDuck {
public static void main(String[] args) {
Duck d1 = new Duck();
d1.setSize(10);
System.out.println(d1.getSize());
Duck d2 = new Duck();
d2.setSize(20);
System.out.println(d2.getSize());
Duck d3 = new Duck();
d3.setSize(30);
System.out.println(d3.getSize());
Duck d4 = new Duck();
d4.setSize(40);
System.out.println(d4.getSize());
System.out.println("静态变量duckCount的值:"+Duck.duckCount);
}
}
结果:
duckCount的值是:1
10
duckCount的值是:2
20
duckCount的值是:3
30
duckCount的值是:4
40
静态变量duckCount的值:4
静态变量的起始动作:类被加载时初始化。
静态项目的初始化有两项保证:
- 静态变量会在该类的任何对象创建之前就完成初始化
- 静态变量会在该类的任何静态方法执行之前就初始化
默认值:主数据类型:0/0.0/false;对象引用:null
如果没有给静态变量赋初值,它会被设定默认值。静态变量的默认值就是该变量类型的默认值。
6、静态的final变量是常数
标记为final的变量,一旦被初始化就不会改动;
类加载后,静态final变量就一直会维持原值。
举例:public static final double PI = 3.141592653589793;
- public 可以供各方读取
- static,不需要Math的实例
- final,类加载后,不变
注意:如何区分变量为不变的常数:都是大写字母!
静态final变量初始化:
方法一:声明的时候
public class Foo{
public static final int FOO_X = 25;
}
方法二:在静态初始化程序中
public class Foo{
public static final double BAR_SIGN;
// 类加载时执行
static{
BAR_SIGN = (double) Math.random();
}
}
如果以上两种方法都没有使用,编译器会报错,变量没有被初始化
7、其他的final
非静态final变量:不能改变它的值。
final的方法:不能被覆盖
final的类:不能被继承
目的:安全
8、Math的常用方法
1)Math.random() 返回0.0~1.0之间的双精度浮点数
double r1 = Math.random();
int r2 = (int) (Math.random() * 5);
2)Math.abs() 返回双精度浮点型参数的绝对值,有覆盖版本
int x = Math.abs(-230); //230
double d = Math.abs(240.45); //240.45
3)Math.round() 返回四舍五入之后的整型或长整型
int x = Math.round(-24.8f);//-25
int y = Math.round(24.45f);//24
4)Math.min() 返回参数中较小的一个
5)Math.max() 返回参数中较大的一个
9、主数据类型的包装类
包装类型
给每个基本类型设计了一个对应的类进行代表,这样八个和基本数据类型对应的类型统称包装类(Wrapper Class),位于 java.lang 包。
基本数据类型 | 包装类 |
---|---|
byte | Byte |
short | Boolean |
int | Integer |
long | Long |
double | Double |
float | Float |
char | Character |
boolean | Boolean |
除了Integer和Character类,其他类都和基本类型一致,只是类名第一个字母大写。
为什么要包装类:
有些地方需要用到对象而不是基本数据类型,比如集合的容器要求元素是 Object类型,因此int、double等类型就benign放进去。
拆箱和装箱
装箱(boxing): 基本数据类型 --> 包装类,实现:包装类.valueof()
拆箱(unboxing):包装类 --> 基本数据类型,实现:包装类对象.xxValue()
举例:
// 装箱
Integer integer = Integer.valueof(1);
// 拆箱
int i = integer.intValue();
自动拆箱和自动装箱
java自动把上述过程实现,原理还是:包装类.valueof() 实现自动装箱;包装类对象.xxValue()实现自动拆箱。
public static void main(String[] args){
Integer integer = 1;//装箱
int i = integer; // 拆箱
}
补充:哪些地方会自动拆装箱
- 场景1:将基本数据类型放入集合类 --》自动装箱
- 场景2:包装类型和基本类型的大小比较 --》自动拆箱,== ,包装类拆箱变成基本数据类型比较
- 场景3:包装类型的计算 --》自动拆箱成基本类型计算
- 场景4:三目运算符的使用 --》第2,第3位操作数分别是基本类型和对象,其中对象会拆箱成基本类型。如果对象是null会发生空指针异常。
- 场景5:函数参数和返回值
10、String 和 primitive主数据类型的互转
1)将String转换成primitive 主数据类型
package chap10;
public class Test02 {
public static void main(String[] args) {
String s = "2";
// String 转换成int
int x = Integer.parseInt(s);
// String 转换成 double
double y = Double.parseDouble("23.45");
// String 转换成 boolean
// Boolean 的构造函数可以取用String来创建对象
boolean b = new Boolean("true").booleanValue();
}
}
有异常的场景:
String t = "two";
int z = Integer.parseInt(t);
编译不报错,运行报错
2)将primitive 主数据类型转换成String
// 主数据类型转换成String
double d = 42.5;
// 方法1:使用 + 操作符
String doubleString = ""+d;
// 方法2:使用静态方法 toString()
String doubleString2 = Double.toString(d);
11、数字的格式化,格式化解析
将数字以带逗号的形式格式化:举例
public class TestFormats {
public static void main(String[] args) {
// 格式设定:将数字以都逗号分开
String s = String.format("%,d",1000000000);
System.out.println(s); // 1,000,000,000
}
}
format("%,d",1000000000) 格式化的两部分:格式指令 + 要格式化的值。
1)格式指令:"%,d" ,说明:% 符号表示把第二个参数放到这里;",d" 该参数要使用的格式
2)要格式化的值:1000000000
含义:把第二个参数以第一个参数所表示带有逗号的整数方式表示。
常用格式化语句语法:
%,d :代表以十进制整数带有逗号的方式表示
%.2f :代表以小数点后两位的方式格式化此浮点数
%,.2f :代表整数部分以有逗号的形式表示,小数部分保留两位
“格式化说明的格式”
必填的只有类型,比如 %d、%f、%x、%c。
有多项参数时:按照顺序对应到格式化设定
int one = 23456567;
double two = 12345.34567;
String result = String.format("the rank is %,d out of %,.2f",one,two);
System.out.println(result);//the rank is 23,456,567 out of 12,345.35
12、日期
日期的格式化:
Date today = new Date();
String t1 = String.format("%tc", today);// 完整的日期和时间
String t2 = String.format("%tr",today);// 只有时间
String t3 = String.format("%tA %tB %td",today,today,today);// 周、月、日
System.out.println("t1: "+ t1);//t1: 星期一 五月 09 17:46:05 CST 2022
System.out.println("t2: "+ t2);//t2: 05:46:05 下午
System.out.println("t3: "+ t3);//t3: 星期一 五月 09
Java.util.Date
适合取得当前时间,如:Data today = new Date();
Java.util.Calendar
1、Calendar 抽象的
抽象类无法new 对象创建实例,但是可以调用其静态方法 getInstance(),会返回继承过Calendar的对象。
Calendar cal = Calendar.getInstance();
大部分java版本会返回 java.util.GregorianCalendar 的实例。
2、运用Calendar对象
- 字段会保存状态
- 日期和时间可以运算
- 日期和时间可以用微秒表示
package chap10;
import java.util.Calendar;
public class TestCalendar {
public static void main(String[] args) {
Calendar c = Calendar.getInstance();
c.set(2022,3,14,18,30);
System.out.println(c);//java.util.GregorianCalendar[time=?,areFieldsSet=false,areAllFieldsSet=true,lenient=true,zone=sun.util.calendar.ZoneInfo[id="Asia/Shanghai",offset=28800000,dstSavings=0,useDaylight=false,transitions=19,lastRule=null],firstDayOfWeek=1,minimalDaysInFirstWeek=1,ERA=1,YEAR=2022,MONTH=3,WEEK_OF_YEAR=20,WEEK_OF_MONTH=2,DAY_OF_MONTH=14,DAY_OF_YEAR=129,DAY_OF_WEEK=2,DAY_OF_WEEK_IN_MONTH=2,AM_PM=1,HOUR=6,HOUR_OF_DAY=18,MINUTE=30,SECOND=0,MILLISECOND=398,ZONE_OFFSET=28800000,DST_OFFSET=0]
// 当前时间以微秒展示
long day1 = c.getTimeInMillis();
System.out.println(day1);//1649932200398
// day1 加上一个小时
day1 += 1000*60*60;
System.out.println(day1);//1649935800398
//加一个小时
c.setTimeInMillis(day1);
System.out.println("new hour:"+c.get(c.HOUR_OF_DAY));//new hour:19
// 加35天
c.add(c.DATE,35);
System.out.println("add 35 days: "+c.getTime());// add 35 days: Thu May 19 19:30:48 CST 2022
//滚动35天
c.roll(c.DATE,35);
System.out.println("roll 35 days: "+c.getTime());//roll 35 days: Mon May 23 19:30:48 CST 2022
// 设置DATE的值
c.set(c.DATE,1);
System.out.println("set to one: "+c.getTime());//set to one: Sun May 01 19:30:26 CST 2022
}
}