1、基本数据类型的包装类
java.lang下提供了8种基本数据类型的包装类类型:Byte、Short、Character、Integer、Long、Float、Double、Boolean,他们的基类都是Number,可以将他们与基本数据类型直接进行操作。
基本类型包装对象代表基本类型,可以直接使用+、- 等操作,但其也属于类类型,应该使用equals()或compareTo()方法或静态方法compare()来比较两个基本类型包装类对象是否相等,compare()方法对于两个数相等返回0,第一个数大于第二个数返回1,小于返回-1。
这8种包装类都提供public方法toString()和静态方法valueOf(),toString()将本对象转换为一个字符串,valueOf()接收一个对应的基本类型或String类型,返回一个包装类对象。String也包含valueOf()静态方法,所以使用valueOf()就可以完成数值和字符串之间的互换。
Integet、Long等类型中有静态成员MAX_VALUE、MIN_VALUE表示其最大和最小值。
class Test
{
public static void main(String[] args)
{
Integer iObj = 10; //Number iObj = 10;
//Integer iObj = new Integer(10);
//Integer iObj = Integer.valueOf(10);
//Integer iObj Integer.valueOf("10");
Double fObj = 3.14 * iObj;
if(fObj > 3.14)
System.out.println(fObj); //输出31.40000000000002
boolean b = Float.isNaN(3.14F); // 是否是一个不正常的数值
Float f = 3.14F;
b = f.isNaN();
double d = iObj.doubleValue(); //获得double类型
Object obj = iObj;
if(obj instanceof Integer)
{
Integer inObj = (Integer)obj;
System.out.println(Integer.compare(iObj, inObj)); //输出为0
}
//signed转换为unsigned
long l = Integer.toUnsignedLong(-1000);//转换为无符号整数
String str = Integer.toUnsignedString(-1000); //转换为无符号整数后再转换为字符串
int i = Integer.divideUnsigned(-1000, -30); //将两个数转换为无符号后计算他们的商
//String和数值之间的转换
double d = Double.valueOf("3.14");
fObj = Double.valueOf(str);
str = String.valueOf(3.14);
str = String.valueOf(fObj);
str = fObj.toString();
str = fObj + "";
//浮点型格式化转换为String
d = 13.615926;
str = new java.text.DecimalFormat("#").format(d); // "14",保留整数位
str = new java.text.DecimalFormat("#.00").format(d); // "13.62",保留两位小数
str = new java.text.DecimalFormat("000.00").format(d); // "013.62",保留三位整数位数
str = new java.text.DecimalFormat("#.00%").format(d); // "1361.59%",转为百分比
str = new java.text.DecimalFormat("#.00E0").format(d); // ".136E2",转为科学技术法
}
}
以下为格式化输出System.out.printf()中的一些格式控制符号:
在jdk 9下可以使用jshell在命令行下直接运行一些简单的代码,如下为设置8个字符宽度,指定2位小数的格式化输出:
下面为输出3+1的和:
一些转义字符:换行'\n'、反斜杠'\\'、单引号'\'',双引号'\"',以十六进制数指定unicode字符输出'\uxxxx',如 System.out.println("\u0048\u0065")为输出"He"字符串。
和C++一样,0x表示十六进制数,0b表示二进制数,如int n = 0x1101,Java 7中还可以使用下划线来连接数字,如int n = 1234_5678。
2、类型转换
如果表达式中包含不同类型,则运算时以最长的类型为主,如下如果去掉a+b之前的强制转换的话会编译不通过:
short a = 1;
long b = 2;
int c = (int)(a + b);
如果操作数都是不大于int的整数,则全部自动提升为int类型,如下如果去掉a+b之前的强制转换的话编译不通过,因为a、b都成了int类型:
short a = 1;
short b = 2;
short c = (short)(a + b);
又如下所示的count+1 > Integer.MAX_VALUE永远不会成立,因为count + 1的值是int类型,int到达最大值后再加1就变成了负数:
int count = 0;
while(1)
{
if(count + 1 > Integer.MAX_VALUE)
......
else
++count;
}
3、Object
Object是所有类的父类,所以所有类型的对象都可以赋给Object类型变量,Object中部分成员函数:
toString():返回"类名+@+hashCode"样式的字符串。很多方法若直接传入对象则会使用对象的toString()返回值,如下使用System.out.println()来直接传入一个对象的时候系统会自动调用对象的toString()方法。很多类都重写了Object的toString()方法,用于返回可表示该对象信息的字符串。
Object o = new Object();
System.out.println(o); //输出java.lang.Object@7d6f77cc
hashCode():返回该对象的hashCode值,默认情况下对象的hash值是根据对象的地址来计算的,类似System的identityHashCode()方法。但很多类重写了该方法,比如String的hashCode()是以字符串内容来计算hash值。
getClass():获得运行时的类,与对应类的静态成员class意义相同。instanceof操作符可以判断一个对象是否是指定类或其子类的对象,如:
boolean b = objectName instanceof className;
finalize():当没有变量引用该对象后,垃圾回收机制会回收该对象,GC在进行回收前会调用对象的该方法,如果在对象回收前有些事情想做,可以重新定义该方法,不过建议的是避免使用该方法。
equals():判断指定对象内的值是否与自己相等,String等很多类都重写了该方法。Object中的equals()作用等同于==,一般子类都是重写equals()方法,如下所示:
public class Test
{
public String name;
public boolean equals(Object obj)
{
if(obj == this) //指向同一对象
return true;
if(obj != null && getClass() == obj.getClass()/*Test.class == obj.getClass()*/ /*obj instanceof Test*/) //同一类型
{
Test t = (Test)obj;
if(t.name.equals(name)) //成员name值相同
return true;
}
return false;
}
public static void main(String[] args)
{
String str1 = "test";
String str2 = new String("test");
System.out.println(str1 == str2); //输出false
System.out.println(str1.equals(str2)); //输出true
}
}
==:对于基本类型是判断两个变量的值是否相等,对于类类型是判断两个引用是否指向同一对象,如下所示,判断两个String的字符串内容是否相同的话应该使用equals()方法:
//s1和s2参考到同一对象,都是字符串池中的String实例
String s1 = "abc";
String s2 = "abc";
//new一定是建立新对象,s3和s4分别参考至新建的String对象
String s3 = new String("abc");
String s4 = new String("abc");
System.out.println(s1 == s2); //true
System.out.println(s1.equals(s2)); //true
System.out.println(s3 == s4); //false
System.out.println(s3.equals(s4)); //true
System.out.println(s1 == s3); //false
System.out.println(s1.equals(s3)); //true
clone();该方法获得当前对象的一个副本,它是浅克隆,即简单复制对象里的变量。clone()的效率很高,比如对于数组,clone比使用工具类Arrays的静态方法copyOf()或System的静态方法arraycopy()来复制数组要快近两倍。因为clone()是protected类型,只能被子类调用或重写,所以我们一般是在自己在该方法中调用clone()方法,如:
class CFoo implements Cloneable //Cloneable为一个标记性的接口,该接口没有定义任何方法。
{
public CFoo clone()throws CloneNotSupportedException
{
return (CFoo)super.clone();
}
}
Object还提供了wait()、notify()等线程控制方法。
Object类的对象都可以与字符串进行+运算,相当于toString()后与字符相加:
Test t = new Test();
System.out.println(t.toString()); //输出Test@61064425
System.out.println(t); //同样输出Test@61064425,直接输出对象相当于调用toString()输出
System.out.println(t + "end"); //输出Test@61064425end
Object可以引用任何类型对象,比如使用Object数组就可以保存不同类型的对象:
Object[] objs = {"java", new Foo(), new Date()};
Object str = "java";
Object f = new Foo();
Object d = new Date();
4、Objects
类似Arrays,Objects工具类提供了一些方法来操作对象,而且这些方法大都是“空指针”安全的,比如对于一个值为null的引用变量来调用toString()的话会引发NullPointerExcetpion异常,而使用Objects则会返回"null",如:
public class Test
{
static Test obj;
public static void main(String[] args)
{
Objects.toString(obj); //输出为null
Objects.hashCode(obj); //输出为0
Objects.requireNonNull(obj, "obj为null"); //引发异常,输出信息"obj为null",requireNonNull的参数不为null则返回参数对象本身,否则引发异常。
}
}
5、final
final修饰变量的时候表示该变量一旦获得了初始值就不能再改变。
final修饰的成员变量必须显示的指定初始值。
final修饰引用类型变量的时候初始值不能改变指的是引用指向的对象不能改变,而该对象final修饰引用类型变量的时候初始值不能改变指的是引用指向的对象不能改变,而该对象的内容可以改变。
final修饰的变量在定义的时候就直接初始化,而且初始化的值在编译的时候就可以确定下来的话,那么使用这个变量的时候就成为了“宏”:
final int N = 5;
final int NUM = 5 * 3;
final String S = "Hello";
final String STR = "Hello" + "World";
final修饰方法表示该方法不能被子类重写。
final修饰类表示该类不能被继承。
6、抽象方法和抽象类
java中的抽象方法类似c++中纯虚函数,它不执行任何程序代码操作,含有抽象方法的类就是抽象类,表示这个类定义不完整,与c++中的抽象类一样,java中抽象类也不能实例化,只能用作父类,抽象类是子类的通用模板,子类可以在抽象类的基础上进行扩展改造,子类中只要有一个抽象方法没有重写这个类就是一个抽象类。abstract用来声明抽象类和抽象方法,抽象类的构造函数主要用来被子类调用。因为抽象类就是用来继承的,所以声明抽象类后就不能再声明类为final类型。因为抽象类不能被实例化,所以抽象类中不能定义静态成员变量和静态方法。
含有抽象方法的类必须使用abstract声明其为抽象类,子类如果没有实现父类抽象方法的话子类也是抽象类,必须使用abstract声明:
abstract class Foo
{
abstract void run();
}
abstract class Dao extends Foo
{
void test() {}
}
class Bar extends Foo
{
@Override
void run() {System.out.println("Bar");}
}
抽象类与多态的结合1, 父类对象调用子类方法:
class SubClass1 extends AbsBase
{
void play(){ System.out.println("SubClass1 play"); }
}
class SubClass2 extends AbsBase
{
void play(){ System.out.println("SubClass2 play"); }
}
public class Main {
public static void main(String[] args){
AbsBase s1 = new SubClass1();
display(s1); //输出"SubClass1 play"
AbsBase s2 = new SubClass2();
display(s2); //输出"SubClass2 play"
}
static void display(AbsBase cla)
{
cla.play();
}
}
抽象类与多态的结合2,抽象类中普通方法调用子类中的方法(抽象方法推迟到子类去实现):
abstract class AbsBase
{
abstract void play();
void test()
{
play();
}
}
class SubClass1 extends AbsBase
{
void play(){ System.out.println("SubClass1 play"); }
}
class SubClass2 extends AbsBase
{
void play(){ System.out.println("SubClass2 play"); }
}
public class Main {
public static void main(String[] args){
AbsBase ba1 = new SubClass1();
AbsBase ba2 = new SubClass2();
ba1.test(); //输出为"SubClass1 play"
ba2.test(); //输出为"SubClass2 play"
}
}
如果一个抽象类中只包含一个抽象方法,那么我们可以在new的时候实现这个抽象类,这样就不用再声明一个具名的子类,相当于是一个实现了抽象接口的匿名类:
abstract class Foo
{
abstract void run();
}
class Bar extends Foo
{
@Override
void run() {System.out.println("Bar");}
}
public class Test
{
public static void main(String[] args)
{
Foo f = new Foo() {
@Override
void run() { System.out.println("Sub Foo"); }
};
f.run();
Foo fo = new Bar() {
@Override
void run() { System.out.println("Sub Bar"); }
};
fo.run();
}
}