先上脑图:
前面说到对象是面向对象程序设计语言的基本单位
,而Java中万物皆对象,任何事物都可以抽象为一个类并实例化对象。但是,我们经常使用的基本数据类型,例如 int ,却不是对象,那么如果想对基本数据类型的变量进行更多的操作,我们该怎么办呢?
例如,我们想要把 String 类型的变量转换为 int 类型,该如何操作呢?或者是在集合中只能存放引用类型的变量,此时是不是就不能对基本数据类型进行操作呢?
于是,Java中把基本数据类型包装成一个类,此时我们就可以定义更多的属性和方法对基本数据类型的变量进行操作,这个类叫做包装类。例如,获取类型的最大值:
Integer.MAX_VALUE;
总的来说,增加包装类的概念有以下的优点:
八种基本数据类型分别对应了八种包装类型,都位于Java.lang
包中。
基本数据类型 | 包装数据类型 |
---|---|
byte | Byte |
short | Short |
int | Integer |
long | Long |
float | Float |
double | Double |
boolean | Boolean |
char | Character |
由于不同包装类的使用方法类似,这里用 Integer 类型举例,其类中封装了很多的属性和方法,这里举例其中最常用的属性和方法,具体内容移步 Java API文档查看。
包装类中常用的属性有包装类对应的基本数据类型以及基本数据类型表示的最大值,最小值。示例:
public class Test {
public static void main(String[] args) {
//常用的属性
System.out.println(Integer.MAX_VALUE);
System.out.println(Integer.MIN_VALUE);
System.out.println(Integer.TYPE);
}
}
包装类中常用的方法是关于 String 类型和基本数据类型,包装数据类型的转换的问题。示例:
public class Test {
public static void main(String[] args) {
//常用的方法
//1.把String类型转换为int类型
String str="123";
int num=Integer.parseInt(str);
//2.把String类型转换为Integer类型
Integer integer=Integer.valueOf(str);
//3.把Integer类型转换为String类型
String s=integer.toString();
}
}
Java 在 JDK 1.5 版本之后,增加了自动装箱和自动拆箱的功能,在这之前,我们要进行手动的转换。示例:
public class Test {
public static void main(String[] args) {
//手动装箱
int a = 10;
Integer integer = new Integer(a);
//手动拆箱
int b = integer.intValue();
}
}
实际上,自动拆箱时,其底层还是调用了 intValue() 方法,而自动装箱时,底层调用的是 valueOf() 方法,相当于也是像手动装箱时 new Integer(a)
,此时,我们只需要阅读 JDK 源码即可明白。
public static Integer valueOf(int i) {
if (i >= IntegerCache.low && i <= IntegerCache.high)
return IntegerCache.cache[i + (-IntegerCache.low)];
return new Integer(i);
}
示例:自动装箱和自动拆箱
public class Test {
public static void main(String[] args) {
//自动装箱
int a = 10;
Integer integer = a;
//自动拆箱
int b = integer;
}
}
其实在我们的程序中还存在很多隐藏的自动装箱和拆箱,例如在使用基本数据类型作为参数传入需要引用变量的方法中,其实发生了自动装箱,在使用包装类型进行运算时,实际上发生了自动拆箱。
示例:
public class Test {
public static void main(String[] args) {
//拆箱
int a=10;
Integer integer=a;
integer++;
//装箱
int b=20;
integer.equals(b);
}
}
字符串类主要包括了 String
,StringBuilder
,StringBuffer
类等。其相同点是都可以对字符串进行操作,底层都是对字符数组 char[] 进行操作。
String 类表示字符串,Java中所有字符串常量值,例如 “abc” ,都是此类的对象。
String 使用了 final 修饰,其不可以被继承,且类的变量值不可以被修改,具有不可变性,String 对象的内容使用字符数组 char[] 来存储。
在数据量比较小的时候,优先使用 String 类,而不是 StringBuilder 类或者 StringBuffer 类。
示例:
public class Test {
public static void main(String[] args) {
// 不同构造方法声明String变量
String str1 = "HelloWorld";
String str2 = new String("HelloWorld");
String str3 = "Hello";
// 长度:length()
System.out.println(str1.length() >= str3.length() ? "str1长" : "str3长");
//获得字符串的第1个字符e
System.out.println(str1.charAt(1));
// 判断是否以指定字符开头
if (str1.startsWith(str3)) {
// str3第一次出现在str1中的位置
int startIndex = str1.indexOf(str3);
// 截取子串
String str1Sub = str1.substring(startIndex, str3.length() - 1);
String str2Sub = str2.substring(startIndex, str3.length() - 1);
System.out.println("两个字符串是否相等:" + str1Sub.equals(str2Sub));
}
}
}
既然已经存在 String 类来操作字符串了,为什么还需要其他两个类呢?我们来看下面的案例:
public class Test {
public static void main(String[] args) {
String str = " ";
for(int i=0;i<100;i++){
str += "hello";
}
}
}
程序在执行第五行时,相当于将原来的 String 类引用指向的对象拿出来与 hello 进行字符相加操作,再存进另一个新的 String 对象中,再让 string 变量指向新生成的对象。其实这样做是非常浪费时间和空间的。
不同于 String 类的是,StringBuilder 类为可变字符串,解决 String 在字符变更方面的低效问题,低层依赖字符数组实现,两者都是 final 修饰的,都不可被继承。
示例:
public class Test {
public static void main(String[] args) {
StringBuilder builder = new StringBuilder("Hello");
System.out.println(builder);
//可以拼接几乎所有能看到的东西,并返回自身
System.out.println(builder.append("World"));
//在指定位置追加字符串
System.out.println(builder.insert(10, "你好"));
//获取指定字符
System.out.println(builder.charAt(2));
//设置某个字符
builder.setCharAt(2,'A');
System.out.println(builder);
//删除指定的字符
builder.deleteCharAt(2);
System.out.println(builder);
//替换指定位置的字符串
System.out.println(builder.replace(9,11,"世界"));
//删除这个区间里的字符串
System.out.println(builder.delete(9,11));
//倒转串
System.out.println(builder.reverse());
//StringBuilder转String
String string = builder.toString();
System.out.println(string);
//String转StringBuilder
StringBuilder builder1 = new StringBuilder(string);
System.out.println(builder1);
}
}
由于 StringBuilder 类不用做线程同步检查,所以存在线程不安全的问题。
StringBuffer 类的构造方法和用法与 StringBuilder 类相同。两者都是可变字符串,可以认为是线程安全的StringBuilder类,做线程同步检查,效率较低。
两者在常用的方法的使用上十分相似,不做赘述。
示例:
//测试String,StringBuilder,StringBuilder的执行速度
public class Test {
public static void main(String[] args) {
String s1="";
StringBuilder s2 = new StringBuilder();
StringBuffer s3 = new StringBuffer();
long l = System.currentTimeMillis();//获取系统时间
for (int i = 0; i < 100000; i++) {//这个是十万次,String实在太慢了还浪费内存,设多了电脑跑不过来
s1=s1.concat("拼接");
}
long l1 = System.currentTimeMillis();
System.out.println("String用时:"+(l1-l));
l = System.currentTimeMillis();
for (int i = 0; i < 1000000; i++) {//下面两个是一百万次
s2.append("拼接");
}
l1 = System.currentTimeMillis();
System.out.println("StringBuilder用时:"+(l1-l));
l = System.currentTimeMillis();
for (int i = 0; i < 1000000; i++) {
s3.append("拼接");
}
l1 = System.currentTimeMillis();
System.out.println("StringBuffer用时:"+(l1-l));
}
}
运行结果:
示例:
public class Test {
public static void main(String[] args) {
StringBuilder builder = new StringBuilder("Test");
System.out.println(builder.length());
//底层数组长度默认为16+传进去的字符串长度
System.out.println(builder.capacity());
for (int i = 0; i < 16; i++) {
builder.append('a');
}
//在数组没装满之前数组长度不变
System.out.println(builder.capacity());
builder.append("123456");
//在长度超出之后,数组会扩容到原来的2倍+2的长度
System.out.println(builder.capacity());
}
}
如上面所示,在 StringBuilder 类的对象中,底层的数组长度默认为传入的字符串长度加 16,在字符串没有达到这个长度时,数组长度不变,如果超出这个长度数组会扩容到原来的 2 倍加 2 的容量。
Math 类中定义了一些数学运算的方法,例如计算最大值,最小值,绝对值等,方便程序的运算。
示例:
public class Test {
public static void main(String[] args) {
System.out.println(Math.max(10,2));//获得最大值
System.out.println(Math.min(10, 5));//获得最小值
System.out.println(Math.abs(-20));//获得绝对值
System.out.println(Math.random());//生成0-1的随机数
}
}
Math 类中虽然为我们提供了产生随机数的方法 Math.random
,但是通常我们需要的随机数范围并不是[0, 1)之间的 double 类型的数据,这就需要对其进行一些复杂的运算。我们可以使用另外一种方式得到随机数,即 Random 类,这个类是专门用来生成随机数的。
示例:
import java.util.Random;
public class Test {
public static void main(String[] args) {
Random random = new Random();
System.out.println(random.nextInt(100));//获取100以内的随机整数
System.out.println(random.nextFloat());//获取浮点随机
}
}
在JDK源码中的 Math.Radom 方法:
private static final class RandomNumberGeneratorHolder {
static final Random randomNumberGenerator = new Random();
}
public static double random() {
return RandomNumberGeneratorHolder.randomNumberGenerator.nextDouble();
}
可以看到,Math.random 底层调用的就是 Random 的nextDouble() 方法。
所有的枚举类型隐性地继承自 java.lang.Enum
。枚举实质上还是类,而每个被枚举的成员实质就是一个枚举类型的实例,他们默认都是 public static final 修饰的,所以我们可以直接通过枚举类型名使用它们。
在定义一组常量时,可以尽量使用枚举类型,枚举就是用来表示有限个数的定值,不能输入别的参数时使用,限制用户输入数据。
示例:
public class Test {
public static void main(String[] args) {
//枚举就是用来表示有限个数的定值,不能输入别的参数时使用,限制用户输入数据
TestAddPerson person = new TestAddPerson("张三", Gender.女);
System.out.println(person);
}
}
class TestAddPerson{
String name ;
Gender gender;
public TestAddPerson(String name, Gender gender) {
this.name = name;
this.gender = gender;
}
public TestAddPerson() {
}
@Override
public String toString() {
return "TestAddPerson{" +
"name='" + name + "\'" +
", gender=" + gender +
'}';
}
public String getName() {
return name;
}
public Gender getGender() {
return gender;
}
}
枚举类型:
public enum Gender {
男,女;
}
Date类 是日期类,位于 java.util.Date包,同时在 java.sql.Date 中也存在日期类,两者同时使用时不能两者同时导包,会出现歧义。
在Java.sql 包中的日期类有三个子类,分别是 Date 类(只含有年月日),Time 类(只含有十分秒)和 TimeStamp 类(时间戳:既含有年月日有含有时分秒)。
示例:
import java.util.Date;
//日期类
public class Test {
public static void main(String[] args) {
Date date = new Date();
System.out.println(date);
//获取1970年到现在的毫秒数
System.out.println(date.getTime());
java.sql.Date date1 = new java.sql.Date(System.currentTimeMillis());
//只有年月日
System.out.println(date1);
String t="2019-8-20";
//将字符串转化为日期
java.sql.Date date2 = java.sql.Date.valueOf(t);
System.out.println(date2);
//只含有时分秒
java.sql.Time time=new java.sql.Time(System.currentTimeMillis());
System.out.println(time);
}
}
DateFormat 表示格式化日期类,format() 方法用于格式化日期,并返回字符串类型。同样,我们还可使用 parse() 方法将字符串型日期转化为日期类。
示例:
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
public class Test {
public static void main(String[] args) throws ParseException {
Date date = new Date();
System.out.println(date);
//创建格式化日期格式为:"yyyy-MM-dd hh:mm:ss"。因为DateFormat是一个抽象类不能new对象,所以new一个他的子类
DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
//格式化这个日期类,返回String字符串
String format = dateFormat.format(date);
//2023-01-11 12:14:42
System.out.println(format);
//这个日期格式一定要和上面注册的格式一样"yyyy-MM-dd hh:mm:ss"
String timer = "2010-12-10 9:15:29";
//将字符串日期转换成日期类,会抛出一个异常
Date parse = dateFormat.parse(timer);
System.out.println(parse);
}
}
Calender 类表示日历类,位于 java.util 包中,日期类和日历类可以相互转化。需要注意的是在获取月份时,一般是从 0 开始的。
示例:
import java.util.*;
public class Test {
public static void main(String[] args) {
//Calendar这也是一个抽象类,需要new他的子类
Calendar calendar = new GregorianCalendar();
System.out.println(calendar);
//获取年月日
System.out.println(calendar.get(Calendar.YEAR));
System.out.println(calendar.get(Calendar.MARCH));//月是从0开始的
System.out.println(calendar.get(Calendar.DATE));
//获取当前月最大天数
System.out.println(calendar.getActualMaximum(Calendar.DATE));
//日期类和日历类相互转化
Date time = calendar.getTime();//日历类转日期类
Calendar calendar1 = new GregorianCalendar();
calendar1.setTime(time);//日期类转日历类
}
}
System 类代表当前 Java 程序的运行平台,程序不能创建 System 类的对象,且该类没有构造方法,System 类提供了一些类变量和类方法,允许直接通过 System 类来调用这些类变量和类方法。
示例:
currentTimeMills():获取当前时间毫秒值
exit(int status):退出JVM,0表示非异常退出
gc():运行垃圾回收处理机制(系统会在某个不确定的时间调用该方法),会调用finalize(),进行垃圾回收
arrayCopy(Object[] srcArr,int srcPos,Object[] desArr,int destPos,int len):数组复制
Java中 File 类位于 java.io 包中,表示文件和文件目录(文件夹)的抽象形式,我们可以使用 File 类中的方法对计算机中的文件和文件夹进行增删查判断等的操作。
Java 属于跨平台运行的语言,所以 File 类的操作与系统无关,任何操作系统都可以使用 File 类来操作计算机的文件和文件夹。File 类用于对文件和文件夹的增删查等操作,与文件的读写无关,如果要进行读写操作,必须要使用 IO 流。
File 类中一共封装了四个成员变量,分别是两个路径分隔符和两个默认名称分隔符:
import java.io.File;
import static java.io.File.*;
public class Test {
public static void main(String[] args) {
System.out.println(pathSeparator);
System.out.println(pathSeparatorChar);
System.out.println(separator);
System.out.println(separatorChar);
}
}
为什么要定义分隔符的静态成员变量呢?
路径中的每级目录之间用一个路径分隔符隔开,而路径分隔符与系统有关,Windows系统使用 \
,而Linux系统使用 /
表示,为了方便Java程序的跨平台运行,所以定义了该静态变量,所以,程序中涉及到分隔符的地方一定要慎用,以免引起错误。
什么是相对路径绝对路径?
绝对路径是一个固定以盘符开始的路径,例如:"C:\\ user \\ Java \\Test.java"
,而相对路径是相对于某一个位置开始的路径,例如,相对于当前项目的根目录:"Test.java"
,不同的系统中,每级目录分隔使用的分隔符不同,Windows 使用 \
,为了避免和转义字符产生冲突,使用 \\
。
在学习一个类是了解他的构造方法是十分重要的:
在 File 类的构造方法中,既可以传入绝对路径,也可以传入相对路径,同时,创建对象时只是把字符串路径的封装在 File 类中,所以不用考虑路径是否真实存在。
示例:
import java.io.File;
public class Test {
public static void main(String[] args) {
File file=new File("C:\\user\\a.txt");
System.out.println(file);
}
}
我们还可以把字符串路径拆分为父路径和子路径,两个路径都可以变化,大大增加了对路径操作的灵活性。
示例:
import java.io.File;
public class Test {
public static void main(String[] args) {
File file=new File("C:\\user\\","a.txt");
System.out.println(file);
}
}
同时,还可以传入一个 File 和一个字符串类型,这样就可以使用 File 类的方法先对路径进行操作,在实例化 File 类对象。
示例:
import java.io.File;
public class Test {
public static void main(String[] args) {
File file=new File("C:\\user\\");
File file1=new File(file,"a.txt");
System.out.println(file1);
}
}
File 用来操作文件和目录,主要进行的操作有获取,判断,增删,遍历等,这里我们先来看一下获取和判断,其中构造方法中传入的路径叫做抽象路径。示例:
import java.io.File;
public class Test {
public static void main(String[] args) {
//获取
File file=new File("C:\\Users\\24091\\Desktop\\Demo\\JavaDemo\\Java.jpg");
File file1=new File("Java.jpg");
//返回此抽象路径名的绝对路径字符串
String absolutePath = file.getAbsolutePath();
String absolutePath1 = file1.getAbsolutePath();
System.out.println(absolutePath);
System.out.println(absolutePath1);
//将此抽象路径名转换为路径名字符串
String path = file.getPath();
String path1 = file1.getPath();
System.out.println(path);
System.out.println(path1);
//返回由此抽象路径名表示的目录或者文件名
String name = file.getName();
String name1 = file1.getName();
System.out.println(name);
System.out.println(name1);
//表示由此抽象路径名表示的文件的长度
long length = file.length();
System.out.println(length);
//判断
//测试此抽象路径名表示的文件或者目录是否存在
File file2=new File("a.txt");
System.out.println(file.exists());
System.out.println(file2.exists());
//测试此抽象路径名表示的是不是目录,前提是该文件或者目录是否存在
File file3=new File("C:\\Users\\24091\\Desktop\\Demo\\JavaDemo");
System.out.println(file.isDirectory());
System.out.println(file3.isDirectory());
//测试此抽象路径名表示的是不是文件,前提是该文件或者目录是否存在
System.out.println(file.isFile());
System.out.println(file3.isFile());
System.out.println(file2.isFile());
System.out.println(file2.isDirectory());
}
}
关于 File 类对系统目录和文件的增删操作重点学习,由于其操作成功一般返回 boolean 值,所以程序两次运行结果可能不同。
示例:
import java.io.File;
import java.io.IOException;
public class Test {
public static void main(String[] args) throws IOException {
/*
创建文件:当且仅当该抽象路径下不存在此文件时创建新的文件
文件不存在,创建文件,返回true;文件存在,不创建,返回false
创建文件的抽象路径必须存在否则抛出异常
*/
File file=new File("C:\\Users\\24091\\Desktop\\Demo\\新建文件.txt");
File file1=new File("C:\\Users\\24091\\Desktop\\Demo\\新建文件");//创建时要看类型,而不是名字
boolean newFile = file.createNewFile();
System.out.println(newFile);
/*
创建文件夹:mkdir创建由此抽象路径名命名的目录;mkdirs同前者,但是可以创建多级目录
文件夹不存在,创建文件夹,返回true;文件夹存在,不创建,返回false;抽象路径名不存在返回false
*/
File file2=new File("C:\\Users\\24091\\Desktop\\Demo\\testDemo");
File file3=new File("C:\\Users\\24091\\Desktop\\Demo\\testDemo.txt");//创建时看类型不看名称
boolean mkdir = file2.mkdir();
System.out.println(mkdir);
File file4=new File("C:\\Users\\24091\\Desktop\\Demo\\testDemo\\11\\22");
boolean mkdirs = file4.mkdirs();
System.out.println(mkdirs);
/*
删除文件和目录:删除由此抽象路径名表示的文件或目录
文件/文件夹存在,删除成功,返回true;文件夹中有内容不会删除,返回false,抽象路径名不存在,返回false
*/
File file5=new File("C:\\Users\\24091\\Desktop\\Demo\\新建文件");
boolean delete = file5.delete();
System.out.println(delete);
File file6=new File("C:\\Users\\24091\\Desktop\\Demo\\testDemo");
boolean delete1 = file6.delete();
System.out.println(delete1);
File file7=new File("C:\\Users\\24091\\Desktop\\Demo\\testDemo\\11\\22");
boolean delete2 = file7.delete();
System.out.println(delete2);
}
}
常见的问题和注意事项都已经注释在程序中,不过多赘述。这里只是举例常用 API 的使用,详细的内容使用参见 API 文档,创作不易,如果你读到这里,说明你已经学会了Java常用类,三连哦!
下期见。
【Java编程进阶】Java抽象类与接口详解
【Java编程进阶】Object类及常用方法详解
【Java编程进阶】Java异常详解