Java 入门学习记录(七)
内部类
内部类就是在一个类的内部再定义一个完整的类
class Outer {
class Inner {
}
}
- 编译之后可生成独立的字节码文件
- 内部类可直接访问外部类的私有成员,而不破坏封装
- 可为外部类提供必要的内部功能组件
成员内部类
在类的内部定义,与实例变量、实例方法同级别的类
外部类的一个实例部分,创建内部类对象时,需要依赖外部类对象
public class Demo {
public static void main(String[] args) {
// 创建外部类对象
Outer outer = new Outer();
// 创建内部类对象
Outer.Inner inner = outer.new Inner();
inner.method();
}
}
当外部类、内部类存在重名属性时,会优先访问内部类属性,若需要访问外部类属性,使用 Outer.this.attribute
成员内部类不能包含静态成员,但可以包含静态常量(内存分配会冲突)
静态内部类
不依赖外部类,可直接创建或通过类名访问,可声明静态成员
可以在内部类前加 static
修饰,不依赖外部类,为外部类提供功能
public class Outer {
private String name = "张三";
private int number = 110;
static class Inner {
private int num = 3;
private String phone = "Apple";
public void method() {
// 调用外部类属性需要先创建外部类对象,因为静态内部类级别与外部类相同
Outer outer = new Outer();
System.out.println(outer.name);
System.out.println(num);
System.out.println(outer.number);
}
}
}
- 只有内部类才可
static
修饰 ,正常类不可
局部内部类
public class Outer {
private String name = "张三";
private int number = 110;
public void method_1() {
// 局部变量
String address = "21313";
// 局部内部类 不能加访问修饰符
class Inner {
private String phone = "1555555555";
public void method_2() {
System.out.println(Outer.this.name); // 访问外部类属性
System.out.println(this.phone); // 访问内部类属性
System.out.println(address); // 访问局部变量,变量自动添加 final 成为常量,因为方法中变量会在运行后消失,只有常量才可以在局部内部类中引用
}
}
// 为了访问局部内部类里面的方法 2,需要在方法 1 中将内部类实例化
Inner inner = new Inner();
inner.method_2();
}
}
匿名内部类
没有类名的局部内部类(特征与局部内部类相同)
必须继承一个父类或者实现一个接口
以实现接口为例,先创建一个名为 Inter 的接口,内部方法为 method
public class Demo {
public static void main(String[] args) {
Inter inter = new Inter() {
@Override
public void method() {
System.out.println("匿名内部类");
}
}; // 注意后面的 ;
inter.method();
}
}
通常创建一个局部内部类是有类名称的,而上述匿名内部类相当于创建了一个没有名字的局部内部类来实现了 Inter 接口,是一个语法合并的形式
Object 类
所有类的直接或间接父类,在继承树的最顶层
未表明 extends
关键字的类,都默认继承 Object 类,并拥有它所定义的方法
Object 类常用方法
getClass 方法
public final Class<?> getClass() {}
返回引用中存储的实际对象类型,通常用于判断两个引用中实际存储对象类型是否一致
Class class = object.getClass();
hashCode 方法
public int hashCode() {}
返回该对象的哈希码
哈希值根据对象地址或字符串或数字使用 hash 算法计算出来的 int 类型的数值
一般情况下,相同对象返回相同的个哈希码值
object.hashCode();
toString 方法
public String toString() {}
返回该对象的字符串表示(表现形式)
可以根据需求重写该方法,如:展示对象的各个属性
obj.toString();
equals 方法
public boolean equals(Object obj) {}
默认实现为(this == obj),比较两个对象地址是否相同
obj1.equals(obj2);
可以进行覆盖,比较两个对象的内容是否相同
覆盖步骤:
- 比较两个引用是否指向同一个对象
- 判断 obj 是否为 null
- 判断两个引用指向的实际对象类型是否一致
- 强制类型转换
- 依次比较各个属性值是否相同
@Override
public boolean equals(Object obj) {
// 比较两个引用是否指向同一个对象
if(this == obj){
return true;
}
// 判断 obj 是否为 null
if(obj == null){
return false;
}
// 判断两个引用指向的实际对象类型是否一致
// if(obj.getClass() == this.getClass()){
//
// }
if(obj instanceof OBJ){
// 强制类型转换
// 依次比较各个属性值是否相同
}
return false;
}
// 对象指向引用不同,内部属性来个相同的指向引用相同 ?
finalize 方法
当对象被判定为垃圾对象时, 由 JVM 自动调用此方法,用以标记垃圾对象,进入回收队列
垃圾对象: 没有有效引用指向此对象时, 为垃圾对象
垃圾回收: 由 GC 销毁垃圾对象, 释放数据存储空间
自动回收机制: JVM 的内存耗尽, 一次性回收所有的垃圾对象
手动回收机制: 使用 System.gc()
; 通知 JVM 执行垃圾回收
ClassName classobj = new ClassName(); // 不回收
new ClassName(); // 回收
包装类
基本数据类型所对应的引用数据类型
Object 可统一所有数据,包装类的默认值是 null
基本数据类型 | 包装类型 |
byte | Byte |
short | Short |
int | Integer |
long | Long |
float | Float |
double | Double |
boolean | Boolean |
char | Character |
装箱和拆箱
装箱就是把基本类型从栈中放入堆中转换成引用类型,反之就是拆箱,如上包装类
public static void main(String[] args) {
// JDK 1.5 之前
// 基本类型
int num = 1;
// 装箱,使用 Integer 类创建对象
Integer integer1 = new Integer(num);
Integer integer2 = new Integer.valueOf(num);
// 拆箱,引用类型转换基本类型
Integer integer3 = new Integer(100);
int num1 = integer3.intValue();
// JDK 1.5之后提供自动装箱和拆箱
int age = 30;
// 装箱 自动装箱相当于引用了 valueOf()
Integer integer4 = age;
// 拆箱 自动拆箱相当于引用了 .intValue()
int age1 = integer4;
}
基本类型和字符串转换
8 种包装类提供不同类型间的转换方式:
- Number 父类中提供的 6 个共性方法
- parseXXX() 静态方法
// 基本类型转换字符串
// 1. 使用 + 号
int num = 255;
String s = num + "";
// 2. 使用 Integer 的 toString() 方法
String s1 = Integer.toString(num);
// 使用 Integer 的 toString() 方法的进制重载
String s2 = Integer.toString(num, 16); // 16进制 Output : ff
// 字符串转换基本类型
String str = "150";
// 使用 Integer 的 parseXXX() 方法
int num1 = Integer.parseInt(str); // 在转换基本类型时注意不要出现非数字
// Boolean 字符串形式转换基本类型只有 "true" 转换成 true, 非 "true" 全都转换成 false
String str1 = "true";
boolean b1 = Boolean.parseBoolean(str1);
Integer 缓冲区
Java 预先创建了 256 个常用的整数包装类型对象
在实际应用当中,对已创建对象进行复用,以节省内存
Integer integer1 = new Integer(100);
Integer integer2 = new Integer(100);
System.out.println(integer1 == integer2); // false
Integer integer3 = 100; // 自动装箱 相当于引用了 valueOf()
Integer integer4 = 100;
System.out.println(integer3 == integer4); // true
Integer integer5 = 200; // 自动装箱 相当于引用了 valueOf()
Integer integer6 = 200;
System.out.println(integer5 == integer6); // false
这是因为自动装箱的 valueOf() 方法所导致的,详细查看 valueOf() 源码,Integer 范围为 -128 ~ 127,200 超过了范围,直接在堆中开辟,false,与内存有关(Cache 数组)
String 类
字符串是常量,创建之后不可改变,修改相当于重写开辟空间,不会修改原来的数据
字符串字面值存储在字符串池中,可以共享
String 比较使用 equals ,在 String 中重写了 equals ,此时比较的不是对象的地址,而是字符串的值
常用方法
- public int length() 返回字符串的长度
- public char charAt(int index) 返回某个位置的字符
- public boolean contains(String str) 判断是否包含某个子字符串
- public char[] toCharArray() 将字符串转换成数组
- public int indexOf(String str) 查找 str 首次出现的下标,存在,则返回该下标;不存在。则返回 -1
- public int lastIndexOf(String str) 查找 str 在当前字符串中最后一次出现的下标索引
- public String trim() 去掉字符串前后的空格,即开头到字符串和结尾之后的字符串,中间的不算
- public String toUpperCase() 将小写转成大写
toLowerCase() 将大写转成小写
- public boolean endsWith(String str) 判断字符串是否以 str 结尾
startsWith(String str) 判断字符串是否以 str 开头
- public String replace(char oldChar, char newChar) 将旧字符串替换成新字符串
- public String[] split(String str) 根据 str 做拆分
补充个字符串位置比较方法
compareTo(str)
若字符串首字符相同,则比较第二个,逐次比较相减,若前一个字符串和第二个子字符串都相同,则比较其长度,长度相减
还有
equalsIgnoreCase(str)
忽略大小写比较值
StringBuffer 和 StringBuilder
StringBuffer 可变长字符串,JDK 1.0 提供,运行效率慢、线程安全
StringBuilder 可变长字符串,JDK 5.0 提供,运行效率快、线程不安全
public class Demo1 {
public static void main(String[] args) {
StringBuffer sb = new StringBuffer(); // StringBuilder 和 StringBuffer 是相同的使用方式
// append() 追加
sb.append("java is nice language");
System.out.println(sb);
sb.append("python life");
System.out.println(sb);
// insert() 添加
sb.insert(0, "在最前面插入字");
System.out.println(sb);
// replace() 替换
sb.replace(0, 5, "hello");
System.out.println(sb);
// delete() 删除
sb.delete(0, 5);
System.out.println(sb);
// 清空 delete
sb.delete(0, sb.length());
}
}
运行结果:
BigDecimal 类
public class Demo {
public static void main(String[] args) {
double d1 = 1.0;
double d2 = 0.9;
System.out.println(d1 - d2);
}
}
思考输出结果,为 0.09999999999999998
因为精度问题,在 Java 中 double 属于近似值存储,精确计算时,使用类
BigDecimal
BigDecimal 类 位于 java.math
中 ,用于精确计算浮点数
public class Demo {
public static void main(String[] args) {
// BigDecimal
BigDecimal bigDecimal1 = new BigDecimal("1.0"); // 注意使用构造体参数为字符串
BigDecimal bigDecimal2 = new BigDecimal("0.9");
BigDecimal answer = bigDecimal1.subtract(bigDecimal2);
System.out.println(answer);
}
}
- 注意使用构造体参数为字符串
- 与减法相同,加法为 add() 方法,其他的方法可以参考说明文档
public class Demo2 {
public static void main(String[] args) {
// 关于除法除不尽报错,需要给出保留小数的位数和具体的舍去方式
// divide(BigDecimal bd, int scale, RoundingMode mode)
BigDecimal result = new BigDecimal("10").divide(new BigDecimal("3"), 5, BigDecimal.ROUND_HALF_UP); // 10 除以 3,保留 5 位小数,采用四舍五入
System.out.println(result); // OutPut : 3.33333
}
}
Date 类
Date 表示特定的瞬间,精确到毫秒。Date 类中的大部分方法都已经被 Calendar 类中的方法所取代
import java.util.Date;
public class Demo3 {
public static void main(String[] args) {
Date date = new Date();
System.out.println(date.toString()); // Tue Apr 13 01:02:17 CST 2021
System.out.println(date.toLocaleString()); // Apr 13, 2021 1:02:17 AM
Date date1 = new Date(date.getTime()-(60*60*24*1000));
System.out.println(date1.toLocaleString()); // Apr 12, 2021 1:02:17 AM
// 方法 after() before()
boolean b = date.after(date1);
System.out.println(b); // true
// 方法 compareTo()
int d = date.compareTo(date1); // date - date1 即今天减去昨天,正为 1,负为 0
System.out.println(d); // 1
// 方法 equals()
boolean b1 = date.equals(date1);
System.out.println(b1); // 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 set(Date date) | 用给定的Date设置此日历的时间 |
Date getTime() | 返回一个Date表示此日历的时间 |
void add(int field, int amount) | 按照日历的规则,给指定字段添加或减少时间量 |
long getTimeMillies() | 毫秒为单位返回该日历的时间值 |
import java.util.Calendar;
public class Demo {
public static void main(String[] args) {
// 创建 Calendar 对象
Calendar calendar = Calendar.getInstance();
System.out.println(calendar.getTime().toLocaleString());
System.out.println(calendar.getTimeInMillis());
// 获取时间信息
// 年
int year = calendar.get(Calendar.YEAR);
// 月
int month = calendar.get(Calendar.MONTH);
// 日
int day = calendar.get(Calendar.DAY_OF_MONTH);
// 时
int hour = calendar.get(Calendar.HOUR_OF_DAY);
// 分
int minute = calendar.get(Calendar.MINUTE);
// 秒
int second = calendar.get(Calendar.SECOND);
System.out.println(year+"年"+(month+1)+"月"+day+"日"+hour+"时"+minute+"分"+second+"秒");
// set() 方法修改时间
calendar.set(Calendar.DAY_OF_MONTH, 5); // set 修改时间
// add() 方法修改时间
calendar.add(Calendar.HOUR, 1); // 当前时间加 1 小时
// 补充方法
int max = calendar.getActualMaximum(Calendar.DAY_OF_MONTH);
int min = calendar.getActualMinimum(Calendar.DAY_OF_MONTH);
System.out.println(max); // 30
System.out.println(min); // 1
// 这里我是 4 月份,5 月的话是 31 1
}
}
输出结果:
SimpleDateFormat 类
SimpleDateFormat 类 是一个以与语言环境有关的方式来格式化和解析日期的具体类
进行格式化(日期 -> 文本)、解析(文本 -> 日期)
常用的时间模式字母
字母 | 时间或日期 | 示例 |
y | 年 | 2021 |
M | 年中月份 | 04 |
d | 月中天数 | 13 |
H | 1 天中小时数(0 - 23) | 01 |
m | 分钟 | 41 |
s | 秒 | 58 |
S | 毫秒 | 365 |
import java.text.SimpleDateFormat;
import java.util.Date;
public class Demo {
public static void main(String[] args) throws Exception{
// 创建 SimpleDateFormat 对象, y 年 M 月
SimpleDateFormat sdf = new SimpleDateFormat("yyyy年MM月dd日");
// 创建 Date
Date date = new Date();
// 格式化 date
String str = sdf.format(date);
System.out.println(str);
// 解析(把字符串转成日期)
Date date1 = sdf.parse("2021年04月13日"); // 使用时需要和前后日期格式一致,否则出现异常
System.out.println(date1);
}
}
System 类
System 类 主要用于获取系统的属性数据和其他操作,构造方法私有
方法名 | 说明 |
static void arraycopy(…) | 复制数组 |
static long currentTimeMillis() | 获取当前系统时间,返回的时毫秒值 |
static void gc() | 建议 JVM 启动垃圾回收器回收垃圾 |
static void exit(int status) | 退出 JVM,参数为 0 表示正常退出,非 0 表示异常退出 |
public class Demo {
public static void main(String[] args) {
// src 源数组,srcPos 从哪个位置开始复制,dest 目标数组,destPos 目标数组的放置位置,length 复制的长度
int[] src = {20, 19, 18, 17, 16, 15, 14, 13};
int[] dest = new int[8];
System.arraycopy(src, 0, dest, 2, 4);
for (int arr: dest) {
System.out.println(arr);
}
System.out.println(System.currentTimeMillis()); // 获取系统当前时间,可以用来计算执行程序用时 ms,开始 - 结束
System.gc(); // 垃圾回收,在另一个类中重写 finalize() 方法,显示是否回收,当没有对象引用可能就会被回收
System.exit(0);
System.out.println("这里不会打印出来,因为上一步已经退出结束了!!!");
}
}