文章目录
一、内部类
概念
在一个类的内部再定义一个完整的类,一般内部类和外部类都会有依赖(层次)关系
//外部类 身体
class Body{
//内部类 头
class Head{}
}
特点
1.编译之后可生成独立的字节码文件。
上述代码在编译之后,除了生成一个Body.class文件,还会生成一个内部类文件Body$Head.class
2.内部类可以直接访问外部类的私有成员,而不破外封装性
class Body{
private int headNum=1;
class Head{
public void show() {
//直接访问,没有问题
System.out.println(headNum);
}
}
}
3.可为外部类提供必要的内部功能组件。
比如Head作为Body的“组件”
分类
成员内部类、静态内部类、局部内部类、匿名内部类
1.成员内部类
特点
(1)在类的内部定义的,与实例变量、实例方法同级别的类。
(2)作为外部类的一个实例部分,创建内部类对象时,必须依赖外部类对象
定义外部类、内部类
public class Outer {
//成员变量
private String name = "李四";
private int age = 20;
private String address = "上海";
private String phone = "119";
//内部类前面也可以加修饰符,与成员变量一样
class Inner {
private String address = "北京";
private String phone = "120";
//方法
public void show() {
System.out.println(name);//外部类变量
System.out.println(age);
System.out.println(address);//内部类变量
System.out.println(phone);
}
}
}
创建实例对象
public static void main(String[] args) {
//创建外部类实例对象
Outer outer = new Outer();
//创建内部类实例对象
Outer.Inner inner = outer.new Inner();
//合在一起可以写成 Out.Inner inner = new Outer().new Inner();
inner.show();
}
(3)当外部类、内部类存在同名属性时,会优先访问内部类属性,就近原则
那么如何在Inner内部类中访问外部类的同名属性呢?
//内部类调用外部类的同名属性
System.out.println(Outer.this.address);
//内部类调用内部类的属性也可以写成
System.out.println(this.address);
(4)成员内部类不能定义静态成员
特例:可以包含final修饰的静态常量
2.静态内部类
特点
(1)在成员内部类的基础之上添加一个static关键字,就变成了静态内部类
(2)不依赖外部对象,可直接创建或通过类名访问,可声明静态成员
其实静态内部类就相当于一个外部类,放在外部类中是为了增强外部类的功能,
只有内部类了才可以用static修饰,外部类不可以
静态内部类想要访问外部类属性,需要先创建外部类的对象
public class Outer {
//成员变量
private String name = "李四";
private int age = 20;
//内部类
static class Inner {
private String address = "北京";
private String phone = "120";
//方法
public void show() {
//调用外部类属性,先创建外部类对象
Outer outer = new Outer();
System.out.println(outer.name);//外部类变量
System.out.println(outer.age);
System.out.println(this.address);//内部类变量
System.out.println(phone);
}
}
}
public static void main(String[] args) {
//直接创建静态内部类实例对象
Outer.Inner inner = new Outer.Inner();
inner.show();
}
这里的new Outer.Inner()
中并没有创建Outer
的对象,只是为了展示其包含关系,类名后面加()
才是通过构造创建了对象
3.局部内部类
特点
(1)类似局部变量,定义在外部类方法中;作用范围和创建对象范围仅限于当前方法
public class Outer {
//成员变量
private String name = "李四";
private int age = 20;
//成员方法
public void show() {
//局部变量
String address = "上海";
//局部内部类
class Inner{
//局部内部类属性
private String phone = "100";
//局部内部类方法
public void show2() {
//可以访问外部类的变量
System.out.println(phone);
}
}
//创建局部内部类对象
Inner inner = new Inner();
inner.show2();
}
}
如果成员方法show()为静态时,其内部类Inner就无法直接访问外部类的资源,需要创建外部类对象
必须在成员方法show()
中创建内部类的对象inner
,才能调用内部类中方法show2()
(2)局部内部类访问外部类当前方法中的局部变量时,因无法保障变量的生命周期与自身相同,变量必须修饰为final,当然,这个变量也无法再被修改
public void show2() {
//可以访问外部类的变量
System.out.println(phone);
//访问外部类中的变量,必须确保外部变量为final
System.out.println(address);
}
这里内部类中的方法show2()
添加调用外部类方法show()
中的局部变量address
,正常情况下,外部方法show
使用完局部变量address
之后,变量就会消失,
但是内部类还需要引用address
,需要address
长期存在,所以必须把变量改为final
修饰,这样内部类对这个常量的引用将不再指向address
,而是直接引用常量
在jdk1.7时,必须手动添加address
被final
修饰,1.8之后,只要我们引用了,就会自动加上final
修饰
(3)局部内部类中不能加静态成员,但是static前面再加上final变成常量就可以了
实际开发当中,局部内部类使用的不多
4.匿名内部类【重点】
特点
(1)没有类名的局部内部类(一切特征都与局部内部类相同)
实际上只是我们定义的时候没有取名字罢了,编译器编译之后会自动取一个名字
(2)必须继承一个父类或者实现一个接口
这不仅是使用的要求,这也是一般情况下使用匿名内部类的目的,因为我们使用匿名内部类就是为了方便创建实例,并调用其中的方法
正常情况下,我们这样使用
/**
* 准备一个接口
*/
public interface USB {
void work();
}
/**
* 一个实现接口的类
*/
public class Mouse implements USB{
@Override
public void work() {
System.out.println("鼠标正在工作。");
}
}
//在另一个类中实例化上面的类,并调用其中的方法
public class testUSB {
public static void main(String[] args) {
USB usb=new Mouse();
usb.work();
}
}
这是正常情况下我们新建一个实现类,可以让我们在不同类中多次使用。但是如果某个实现类只需要用到一次,之后就不会再用了,如果还是单独建一个class来实现的话是否麻烦且多余了?这时候就可以简化成局部内部类
public class testUSB {
public static void main(String[] args) {
//创建局部内部类
class Phone implements USB{
@Override
public void work() {
System.out.println("手机已连接。");
}
}
//实例化
Phone phone=new Phone();
phone.work();
}
}
因为实现类只使用一次,类名看起来也是多余的,那么再精简优化一下就变成了匿名内部类
也可以理解为直接把USB
接口实例化了,虽然接口无法创建对象
public class testUSB {
public static void main(String[] args) {
//在new的接口中实现方法,这里也可以是一个抽象方法或者一个父类
USB phone=new USB() {
@Override
public void work() {
System.out.println("手机已连接。");
}
};
phone.work();
}
}
(3)它是定义类、实现类、创建对象的语法合并,只能创建一个该类的对象
(4)优点是减少代码量,缺点是可读性差
我们可以查看一下本地生成的class文件,发现会一个testUSB$1.class文件,这个1就可以理解是匿名内部类的名字
二、Object类
特点
(1)超类、基类,所有类的直接或间接父类,位于继承树的最顶层。
(2)任何类,如果没写extends关键字继承某个类,那么默认的就是直接继承Object类。
(3)Object类中所定义的方法,是所有对象都具备的方法。
(4)Object类型可以存储任何对象
作为参数,可接受任何对象。
作为返回值,可返回任何对象
方法
getClass()
public final Class<?> getClass(){...}
返回引用中存储的实际对象类型。
应用:通常用于判断两个引用中实际存储对象类型是否一致
public class Student {
private String name;
private int age;
public Student() {
}
public Student(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
public class TestClass2 {
public static void main(String[] args) {
Student s1 = new Student("a",20);
Student s2 = new Student("b",21);
//判断student1和studen2是否同一个类
if (s1==s2) {
System.out.println("s1和s2是同一个类型");//输出结果
} else {
System.out.println("s1和s2不是同一个类型");
}
}
}
hashCode()
pubilc int hashCode(){...}
返回该对象的哈希码值
哈希值根据对象的地址或字符串或数字使用hash算法计算出来的int类型的数值
一般情况下相同对象返回相同哈希码
toString()
public String toString(){...}
返回该对象的字符串表示(表现形式)。
默认返回的是类名和一个十六进制表示的哈希值,也就是
getClass().getName() + ‘@’ + Integer.toHexString(hashCode())。
可以根据程序需求覆盖/重写该方法,如展示对象的各个属性值
上面的s1对象如果使用toString打印结果和直接打印s1没有区别
public class TestClass2 {
public static void main(String[] args) {
Student s1 = new Student("a",20);
System.out.println(s1.toString());
}
}
得到Student类的全名+@+s1的哈希值,和和
查看toString源码
public String toString() {
return getClass().getName() + "@" + Integer.toHexString(hashCode());
}
实际上我们更希望看到这个对象的属性,所以通常我们会重写toString()方法,可以直接IDE工具生成,右键-gengerate-toString()
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
equals()
public boolean equals(Object obj){...}
默认实现为(this==obj),比较两个对象地址是否相同。
可进行重写,比较两个对象的内容是否相同
面试常考equals和==的区别
重写equals()一般固有的步骤:
1.比较两个引用是否指向同一个对象。
2.判断obj是否为null。
3.判断两个引用指向的实际对象类型是否一致。
4.强制类型转换。
5.依次比较各个属性值是否相同。
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Student student = (Student) o;
return this.age == student.age && this.name == student.name;
}
finalize()
当对象被判定为垃圾对象时,由JVM自动调用此方法,用以标记垃圾对象,进入回收队列
什么是垃圾对象?
1.垃圾对象
没有有效引用指向此对象时,为垃圾对象
2.垃圾回收
由GC销毁垃圾对象,释放数据存储空间
3.自动回收机制
JVM的内存耗尽,一次性回收所有垃圾对象
4.手动回收机制
使用System.gc();通知JVM执行垃圾回收
这个方法实际上是不包含任何代码,它是一个空的方法,你可以重写该方法来观察JVM是否回收了某些对象,比如添加打印输出
protected void finalize() throws Throwable { }
重写,如果该方法执行了, 说明垃圾被回收了
@Override
protected void finalize() throws Throwable {
System.out.println("垃圾被回收");
}
三、包装类
概念
基本数据类型所对应的引用数据类型
Object可统一所有数据,包装的默认值为nul
为了能让基本类型能功能更强大,将基本类型变为引用类型,他们就具备了属性和方法,基本类型数据存在栈中,变为引用类型后就可以存放在堆里了
基本数据类型 包装类型 对应关系
基本数据类型 | 包装类 |
---|---|
byte | Byte |
short | Short |
int | Integer |
long | Long |
float | Float |
double | Double |
boolean | Boolean |
char | Character |
类型转换与装箱、拆箱
每个基本类型都对应着一个引用(包装)类型,基本类型存储在栈空间而引用类型存储在堆空间;把基础类型转换成引用类型的过程叫做装箱,每个引用类型都提供了一些方法和属性可供使用;把引用类型转换成基础类型的过程叫做拆箱。
其中Byte
Short
Integer
Long
Float
Double
都继承了父类Number
拆箱
Number
中有byteValue()
shortValue()
intValue()
longValue()
doubleValue()
floatValue()
可以将引用类型转为基本类型,这个过程叫拆箱
装箱
/**
* 拆箱和装箱演示
*/
public class Demo1 {
public static void main(String[] args) {
//装箱(两种方法)
int num1=10;
Integer integer1=new Integer(num1);
Integer integer2=Integer.valueOf(num1);
//拆箱
int num2=integer1.intValue();
}
}
在JDK1.5之前,我们需要进行如上的操作来装箱和拆箱,但在JDK1.5之后,java就提供了自动装箱和拆箱的功能,直接赋值
public static void main(String[] args) {
//装箱
int num1=10;
Integer integer1=num1;
//拆箱
int num2=integer1;
}
不必调用方法传值便可以实现装箱和拆箱。但实际上并不是不需要写,而是编译的时候java自动帮你做了这些工作。我们可以使用一个小工具Xjad来验证一下,这是一个反编译的工具,可以把class文件反编译成java代码,如果有需要可以自己百度下,这里只简单描述一下。运行上一段代码,将编译的class文件拖到这个小工具中,可以看到:
public static void main(String args[])
{
int num1 = 10;
Integer integer1 = Integer.valueOf(num1);
int num2 = integer1.intValue();
}
字符串、基本类型之间转换
8种包装类提供不同类型间的转换方式。
Number父类中提供的6个共性方法。
parseXXX()静态方法。
valueOf()静态方法。
public class Demo2 {
//基本类型和字符串之间的转换
public static void main(String[] args) {
//1.基本类型转换成字符串
int n1=255;
//1.1 使用+号
String s1=n1+"";
//1.2 使用Integer中的tostring方法
String s3=Integer.toString(n1);
String s2=Integer.toString(n1, 10);//第二个参数是基数,可以理解为x进制
//2.字符串转换成基本类型
String string="150";
int n2=Integer.parseInt(string);
//字符串转换成boolean类型,"true"->"true" "非true"->"false"
String string2="true";
String string3="123";
boolean b1=Boolean.parseBoolean(string2);//true
boolean b2=Boolean.parseBoolean(string3);//false
}
}
整数缓冲区
Java预先创建了256个常用的整数包装类型对象
public class Demo2 {
public static void main(String[] args) {
//返回false
Integer integer1=new Integer(100);
Integer integer2=new Integer(100);
System.out.println(integer1==integer2);
//返回true
Integer integer3=100;
Integer integer4=100;
System.out.println(integer3==integer4);
//返回false
Integer integer5=200;
Integer integer6=200;
System.out.println(integer5==integer6);
}
}
第一个,两个存储在栈空间的变量分别指向的是两个堆空间中的对象,两个对象的地址不一样,所以在比较时返回了false
第二个、第三个都是进行了自动装箱,但是结果不一样,首先清楚,自动装箱其实底层调用了valueOf()方法而不是构造方法,通过上面的反编译工具可以看到,
我们再查看valueOf()源码
public static Integer valueOf(int i) {
if (i >= IntegerCache.low && i <= IntegerCache.high)
return IntegerCache.cache[i + (-IntegerCache.low)];
return new Integer(i);
}
进入到IntegerCache中
private static class IntegerCache {
static final int low = -128;
static final int high;
static final Integer cache[];
static {
// high value may be configured by property
int h = 127;
String integerCacheHighPropValue =
sun.misc.VM.getSavedProperty("java.lang.Integer.IntegerCache.high");
if (integerCacheHighPropValue != null) {
try {
int i = parseInt(integerCacheHighPropValue);
i = Math.max(i, 127);
// Maximum array size is Integer.MAX_VALUE
h = Math.min(i, Integer.MAX_VALUE - (-low) -1);
} catch( NumberFormatException nfe) {
// If the property cannot be parsed into an int, ignore it.
}
}
high = h;
cache = new Integer[(high - low) + 1];
int j = low;
for(int k = 0; k < cache.length; k++)
cache[k] = new Integer(j++);
// range [-128, 127] must be interned (JLS7 5.1.7)
assert IntegerCache.high >= 127;
}
private IntegerCache() {}
}
结论:Integer缓冲区是一个-127~128的数组cache = new Integer[(high - low) + 1];
果我们valueOf()里面的数值在缓存范围内,将直接返回缓存数组中的包装类对象
所以第二个返回的是缓存内的对象,第三个返回的是新创建的两个对象
这么设计是因为这些数在开发时常用,创建缓冲区可以提高效率
四、String类
特点
第一种创建方式
字符串是常量,创建之后不可变
字符串字面值存储在字符串池中,可以共享
public static void main(String[] args) {
String name = "hello";
name = "zhangsan";
String name2 = "zhangsan";
}
第一个name创建,在常量池中存入hello,然后指向name引用
第二name,在常量池中创建zhangsan,name指向新的常量,并没有修改原来的hello
第三个name2,创建时发现常量池中有zhangsan,所以也直接指向常量zhangsan
所以修改字符串其实是在常量池中重新开辟空间
第二种字符串创建方式
String str = new String("java");
这种方式,既创建了常量,也创建了对象,栈内存中的str指向堆中的对象,对象又指向了常量
所以这种创建方式也浪费了空间
如果我们又创建了对象str2
String str2 = new String("java");
str2指向了一个新的对象,新的对象又指向了同一个常量
字符串的equals和==【重点】
结论:
1.当我们直接用字符串赋值给变量时,如果字符串字面值相同,则变量==返回true,因为两个变量都指向同一个常量
public static void main(String[] args) {
String s1 = "hello";
String s2 = "hello";
System.out.println(s1==s2);//返回true
}
2.当我们用new String的方式创建字符串并赋值给变量时,两个变量==将返回false,因为他们指向了堆内存中不同的对象
public static void main(String[] args) {
String s1 = new String("hello");
String s2 = new String("hello");
System.out.println(s1==s2);//返回false
}
3.所以,当我们比较两个字符串的时候,推荐使用equals,因为String内部重写了equals方法,比较的是字面值,==比较则不一定,要看字符串的创建方式
public static void main(String[] args) {
String s1 = new String("hello");
String s2 = new String("hello");
System.out.println(s1.equals(s2));//返回true
}
常用方法
返回字符串长度
public int length()
根据下标获取字符串
public char charAt(int index)
判断当前字符串是否包含str
public boolean contains(String str)
将字符串转为数组
public char[] toCharArray()
查找str首次出现的下标,存在则返回下标,不存在则返回-1
public int indexOf(String str)
查找字符串在当前字符串中最后一次出现的下标索引
public int lastIndexOf(String str)
去掉字符串前后空格,不包含中间的
public String trim();
将小写转为大写
public String toUpperCase()
判断字符串是否以str结尾
public boolean endsWith(String str)
判断字符串是否以str开头
public boolean startsWith(String prefix)
旧字符串替换为新的字符串
public String replace(char oldChar, char newChar)
根据str做拆分
public String[] split(String regex)
忽略大小写比较
public boolean equalsIgnoreCase(String anotherString)
compare()比较两个字符串在编码表里面的位置,只比较第一个字符的编码大小
如果前面的字符串都一样,但是另一个字符串有更多的字符,则比较二者长度
返回的是编码值的差值,或者长度差
public int compareTo(String anotherString)
案例演示
需求:
已知String str = “this a text”;
1.将str中的单词单独获取
2.将str中的text替换为practice
3.在text前面插入一个easy
4.将每个单词的首字母改为大写
public class TestString {
public static void main(String[] args) {
String str = "this a text";
String[] arr = str.split(" ");
for (String s : arr) {
System.out.println(s);
}
String str2 = str.replace("text", "practice");
System.out.println(str2);
str.replace("text", "easy text");
System.out.println(str);
for (int i=0;i< arr.length;i++) {
char first = arr[i].charAt(0);
char upperFirst = Character.toUpperCase(first);
String str3 = upperFirst+arr[i].substring(1);
System.out.println(str3);
}
}
}
五、可变字符串 StringBuffer StringBuilder
特点
String的特性决定了我们在使用时可能会产生大量的垃圾数据,效率比较低,
Sun公司开发了两个新的类,相当于String的增强类,效率更高,方法也更加强大
这两个类相当于String的增强类,事先开辟了一块缓冲区;这两个类的用法是一样的,效率都比String高
StringBuffer:可变长字符串,JDK1.0提供,运行效率慢、线程安全。
StringBuilder:可变长字符串,JDK1.5提供,运行效率快、线程不安全
演示
/**
* 演示StringBuilder常用方法的使用
* 效率比String高;比String节省内存
*/
public class Demo1 {
public static void main(String[] args) {
StringBuilder stringBuilder=new StringBuilder();
//1. append();追加
stringBuilder.append("我");//我
stringBuilder.append("菜");//我菜
//2. insert();插入
stringBuilder.insert(0, "前");//前我菜
//3. replace();替换
stringBuilder.replace(1, 2, "你");//前你菜
//4. delete();删除
stringBuilder.delete(0, 1);//你菜
System.out.println(stringBuilder.toString());
}
}
上面注意含头不含尾
六、BigDecimal类
特点
double b1=1.0;
double b2=0.9;
System.out.print(b1-b2);
以上结果输出并不是0.1,而程序输出的结果是0.0999…98。因为浮点类型存储的实际是一个近似值,经过计算之后肯定会有误差,只不过这种误差很小
很多实际应用中需要精确计算,用double肯定不符合要求,这时候需要借助BigDecimal类来实现
演示
/**
* 演示BigDecimal类的使用
*/
public class Demo1 {
public static void main(String[] args) {
BigDecimal b1=new BigDecimal("1.0");
BigDecimal b2=new BigDecimal("0.9");
System.out.println(b1.subtract(b2));//减法 0.1
System.out.println(b1.add(b2));//加法 1.9
System.out.println(b1.multiply(b2));//乘法 0.90
System.out.println(b2.divide(b1));//除法 0.9
}
}
注意
1.传参的时候使用字符串
2.如果除法的结果除不尽,那么就会报一个异常;所以在除法运算的时候需要使用divide的另外一个重载方法divide(divisor, scale, roundingMode)
参数divisor:除数
参数scale:指定精确到小数点后几位
参数roundingMode:指定小数部分的取舍模式,通常采用四舍五入的模式。取值为BigDecimal.ROUND_HALF_UP
七、Date类
特点
Date表示特定的瞬间,精确到毫秒。Date类中的大部分方法都已经被Calendar类中的方法所取代(已过时)
演示
创建时用util包的,不要使用sql包的
/**
* 演示Date类中尚未过时的方法
*/
public class Demo1 {
public static void main(String[] args) {
Date d1=new Date();
Date d2=new Date();
//当前时间 Wed Nov 04 12:01:52 CST 2020
System.out.println(d1.toString());
//已过时,打印当地时间
//2020-11-4 12:01:52
System.out.println(d1.toLocaleString());
//after before判断两个时间前后关系
d2=new Date(d1.getTime()-60*60*24*1000);//昨天此刻
System.out.println(d1.after(d2));//true 今天在昨天后面
System.out.println(d1.before(d2));//false
//compareTo比较,两者毫秒数相减,返回正负0
System.out.println(d1.compareTo(d2));//1
System.out.println(d2.compareTo(d1));//-1
System.out.println(d1.compareTo(d1));//0
//equals判断是否相等
System.out.println(d1.equals(d2));//false
}
}
八、Calendar类
特点
Calendar提供了获取或设置各种日历字段的方法
方法
构造方法protected Calendar()
由于修饰符是protected,所以无法直接创建该对象,而是使用static Calendar getInstance()
,表示使用默认时区和区域获取日历
void set(int year,int month,int date,int hourOfDay,int minute,int second)
设置日历的年、月、日、时、分、秒。
int get(int field)
返回给定日历字段的值。字段比如年、月、日等。
void setTime(Date date)
用给定的Date设置次日历的时间。Date->Calendar
Date getTime()
返回一个Date表示此日历的时间。Calendar->Date
void add(int field,int amount)
按照日历的规则,给指定字段添加或减少时间量。
long getTimeMillies()
毫秒为单位返回该日历的时间值。
/**
* 演示Calendar类常用方法的使用
*/
public class Demo2 {
public static void main(String[] args) {
//创建Calendar对象
Calendar calendar=Calendar.getInstance();
//2020-11-4 13:50:56
System.out.println(calendar.getTime().toLocaleString());
//1604469056368
System.out.println(calendar.getTimeInMillis());
//获取时间信息
//年 2020
System.out.println(calendar.get(Calendar.YEAR));
//月 11 值为0-11
System.out.println(calendar.get(Calendar.MONTH)+1);
//日 4
System.out.println(calendar.get(Calendar.DAY_OF_MONTH));
//小时 1/13 12小时/24小时
System.out.println(calendar.get(Calendar.HOUR));
System.out.println(calendar.get(Calendar.HOUR_OF_DAY));
//分钟 50
System.out.println(calendar.get(Calendar.MINUTE));
//秒 56
System.out.println(calendar.get(Calendar.SECOND));
//修改时间
calendar.set(Calendar.YEAR, 2019);
//2019-11-4 13:50:56
System.out.println(calendar.getTime().toLocaleString());
//添加或减少时间量
calendar.add(Calendar.MONTH, -1);
//2019-10-4 13:50:56
System.out.println(calendar.getTime().toLocaleString());
//获取时间字段的最大值、最小值
//31 / 1
System.out.println(calendar.getActualMaximum(Calendar.DAY_OF_MONTH));
System.out.println(calendar.getActualMinimum(Calendar.DAY_OF_MONTH));
}
}
九、SimpleDateFormat类
特点
SimpleDateFormat是一个以语言环境有关的方式来格式化和解析日期的具体类。
可以进行格式化(日期到文本)和解析(文本和日期)。
常用的时间模式字母:
字母 | 日期或时间 |
---|---|
y | 年 |
M | 年中月份 |
d | 月中天数 |
H | 1天中小时数0-23 |
m | 分钟 |
s | 秒 |
S | 毫秒 |
public class Demo3 {
public static void main(String[] args) throws ParseException {
//创建对象
SimpleDateFormat simpleDateFormat=new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");
//创建Date对象
Date date=new Date();
//格式化
String string=simpleDateFormat.format(date);
//2020/11/04 14:10:21
System.out.println(string);
//解析(必须按照上面格式化的形式)
Date date2=simpleDateFormat.parse("1999/01/30 00:00:00");
//1999-1-30 0:00:00
System.out.println(date2.toLocaleString());
}
}
十、System类
特点
系统类,主要用于获取系统的属性数据和其他操作,其构造方法是私有的。直接使用里面的静态方法
常用方法
static void arraycopy(...)
复制数组
static long currentTimeMillis()
获取当前系统时间,返回的是毫秒值。通常用来计算某个操作的用时,操作前后各获取一个时间然后相减,所得的毫秒数就是用时。
static void gc()
建议JVM赶快启动垃圾回收器回收垃圾,具体是否调用是由系统决定的。
static void exit(int status)
退出JVM,如果参数是0表示正常退出JVM,非0表示异常退出JVM。
/**
* 演示arraycopy的使用
*/
public class Demo1 {
public static void main(String[] args) {
int[] src= {3,14,15,92,65,35,85};
int[] dest = new int[7];
int srcPos=0,destPos=0;
int length=src.length;
/**
* src:源数组
* scrPos:复制的源数组起始位置
* dest:目标数组
* destPos:复制的目标数组起始位置
* length:复制的数组长度
*/
System.arraycopy(src, srcPos, dest, destPos, length);
}
}
在Arrays类里也有一个copyOf复制数组的方法,这两个方法有什么区别呢?通过查看源码发现Arrays的这个方法内部调用的就是System.arraycopy()方法,而arraycopy方法的源码是被native修饰的本地方法:
public static native void arraycopy(Object src, int srcPos,
Object dest, int destPos,
int length);
可以看到方法体是空的,它是一个原生函数,并不是由java来实现的,而是由c/c++来实现的,java只是调用了它,由c++实现的这个方法效率会比java快很多