1. 内部类
如果一个事物内部包含另一个事物,那么这就是一个类内部包含另一个类。
例如:身体和心脏的关系。
分类:
- 成员内部类。
- 局部内部类(包含匿名内部类)。
1.1 成员内部类的定义
定义格式:
修饰符 class 外部类名称 {
修饰符 class 内部类名称 {
}
}
注意: 内用外,随便访问,外用内,需要一个内部类的成员对象。
//外部类
public class Body {
//成员内部类
public class Heart {
//内部类的方法
public void beat() {
System.out.println("心脏蹦蹦跳!");
System.out.println("name");
}
}
//外部类的成员变量
private String name;
//外部类的方法
public void methodBody() {
System.out.println("外部类的成员变量");
Heart heart = new Heart();
heart.beat();
}
public String getName() {
return name;
}
public void setName() {
this.name = name;
}
}
1.2 成员内部类的使用
- 间接方法:在外部类的方法当中,使用内部类,然后main只是调用外部类的方法。
- 直接方式:
格式: 外部类名称.内部类名称 对象名 = new 外部类名称().new 内部类名称();
public class InnerClass {
public static void main(String[] args) {
Body body = new Body(); //创建外部类对象
//通过外部类的对象,调用外部类的方法,立面间接使用内部类Heart
body.methodBody;
//直接方法
Body.Heart heart = new Body().new Heart();
heart.beat();
}
}
1.3 内部类的同名变量访问
如果出现了重名,格式为:外部类名称.this.外部类成员变量名;
public class Outer {
int num = 10;
public class Inner {
int num = 20;
public void methodInner() {
int num = 30;
System.out.println(num); //30,就近原则
System.out.println(this.num); //20
System.out.println(Outer.this.num); //10
}
}
}
1.4 局部内部类定义
如果一个类是定义在一个方法内部的,那么它就是局部内部类。
“局部”,只有当前所属的方法才能使用它,出了这个方法就不能使用了。
定义格式:
修饰符 class 外部类名称 {
修饰符 返回值类型 外部类方法名称(参数列表) {
class 局部内部类名称 {
}
}
}
例:
public class Outer {
public void methodOuter() {
//局部内部类
class Inner {
int num = 10;
public void methodInner() {
System.out.println(num); //10
}
}
Inner inner = new Inner();
inner.methodInner;
}
}
使用:
public class DemoMain {
public static void main(String[] args) {
Outer outer = new Outer();
outer.methodOuter();
}
}
类的修饰符:public > protected > (default) > private
定义一个类的时候,权限修饰符规则:
- 外部类:public / (default)
- 成员内部类:public / protected / (default) / private
- 局部内部类:什么都不能写
1.5 局部内部类的final问题
局部内部类如果希望访问所在方法的局部变量,那么这个局部变量必须是 有效final的 。
从Java 8开始,只要局部变量事实不变,那么final关键字可以省略。
原因:
- new出来的对象在堆内存当中。
- 局部变量是跟着方法走的,在栈内存中。
- 方法运行结束后,立刻出栈,局部变量就会立刻消失。
- new出来的对象会在堆中持续存在,知道垃圾回收消失。
1.6 匿名内部类
如果接口的实现类(或者父类的子类)只需要使用唯一的一次。那么就可以省略掉该类的定义,而改为使用【匿名内部类】;
匿名内部类的定义格式:
接口名称 对象名 = new 接口名称(){
//覆盖重写所有的抽象方法
};
例:
public class DemoMain {
public static void main(String[] args){
MyInterface obj = new MyInterface() {
@Override
public void method() {
System.out.println("匿名内部类实现了方法");
}
};
obj.method();
//如果该实现类对象只使用一次
new MyInterface() {
@Override
public void method() {
System.out.println("匿名内部类实现了方法");
}
}.method();
}
}
1.7 匿名内部类的注意事项
对格式“new 接口名称(){…}”进行解析:
- new代表创建对象的动作;
- 接口名称就是匿名内部类需要实现的那个接口;
- {…}才是匿名内部类的内容;
- 匿名内部类,在创建对象的时候,只能使用唯一一次。
如果希望多次创建对象,而且类的内容一样的话,那么就必须单独定义的实现类了。 - 匿名对象,在调用方法的时候,只能调用唯一一次。
如果希望同一个对象,调用多次方法,那么必须给对象起个名字。 - 匿名内部类是省略了【实现类/子类名称】,匿名对象省略了【对象名称】。
2. Object类和Objects类
- java.lang.Object类是Java语言中的根类,即所有类的父类。
- 如果一个类没有特别指定父类,那么默认Object为父类。
2.1 Object类的toString方法
格式: 对象名.toString();
作用: 返回该对象的地址值,返回值为String类型。
注意: 直接打印对象的名字,其实就是在调用对象的toString方法
String s = p.toString();
System.out.println(s);
//作用一样
System.out.println(p);
2.2 toString方法的覆盖重写
重写toString方法,使它返回对象的属性
public class Person {
String name;
int age;
public Person() {
}
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 Person(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
public class DemoToString {
public static void main(String[] args) {
Person p = new Person("张三", 18);
System.out.println(p.toString()); //Person{name='张三', age=18}
}
}
注意: 看一个类是否重写了toString方法直接打印这个类对应的对象名称即可;若没有重写,那么打印的就是该对象的地址值。
2.3 Object类的equals方法
格式: 对象名1.equals(对象名2);
作用: 比较两对象是否“相等”,返回值为boolean类型;
注意: 比较的是两对象的地址值。
public class DemoEquals{
public static void main(String[] args){
Person p1 = new Person("张三", 18);
Person p2 = new Person("李四", 17);
boolean b = p1.equals(p2);
System.out.println(b); //false
}
}
2.4 equals方法覆盖重写
重写equals方法,使它比较两对象的属性
......
@Override
public boolean equals(Object obj) {
//若obj为null,直接返回false,提高程序效率
if(obj == null){
return false;
}
//若obj就是this,直接返回true
if(obj == this){
return true;
}
//向下强制类型转换
if(obj instanceof Person){
Person p = (Person)obj;
boolean b = p.name.equals(this.name) && p.age == p.age;
return b;
}
//若obj不是Person类型的,直接返回false
return false;
}
......
可以Alt + Insert快捷键直接生成
......
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Person person = (Person) o;
return age == person.age &&
Objects.equals(name, person.name);
}
@Override
public int hashCode() {
return Objects.hash(name, age);
}
......
2.5 Objects类的equals方法
格式: Objects.equals(对象名1, 对象名2);
作用: 对两个对象进行比较,防止空指针异常,返回值为boolean类型。
原理:
public static boolean equals(Object a, Object b){
return (a == b) || (a != null && a.equals(b));
}
注意: 需要导包 import java.util.Objects;
import java.util.Objects;
public class DemoObjects {
public static void main(String[] args){
String s1 = "abc";
String s2 = "abc";
boolean b = Objects.equals(s1, s2);
System.out.println(b); //true
}
}
3. 日期时间类
3.1 毫秒值的概念和作用
- java.util.Date;表示日期和时间的类,需要导包。
- 类Date表示特点的瞬间,精确到毫秒。
- 毫秒:千分之一秒;
- 毫秒值的作用:可以对时间和日期进行计算(把日期转化成毫秒,计算完再把毫秒转换成日期)
- 时间原点(0毫秒):1970年1月1日00:00:00(英国格林威治时间)
- 注意:中国位于东八区,会把时间增加8小时(即1970年1月1日08:00:00)
3.2 Date类的构造方法和成员方法
Date类的空参数构造方法:
Date()获取的是当前系统的日期和时间;
格式: Date 对象名 = new Date();
Date类的带参数构造方法:
Date(long date)传递毫秒值,把毫秒值转换为Date日期
注意: 毫秒值后加L
格式: Date 对象名 = new Date(毫秒值L);
Date类的成员方法:
long getTime();把日期转化成毫秒(相当于System.currentTimeMillis());
返回自1970.1.1 00:00:00 GMT以来此Date对象表示的毫秒值。
格式: long 对象名 = 对象名.getTime();
import java.util.Date;
public class DemoDate {
public static void main(String[] args) {
demo01();
demo02();
demo03();
}
public static void demo01(){
Date date = new Date();
System.out.println(date);
}
public static void demo02(){
Date date = new Date(12345L);
System.out.println(date);
}
public static void demo03(){
Date date = new Date();
long time = date.getTime();
System.out.println(time);
}
}
3.3 DateFormat类 & SimpleDateFormat类介绍
java.text.DateFormat; 是日期时间格式化子类的抽象类
作用:
- 格式化:把日期变成文本;
- 解析:把文本变成日期。
成员方法:
- String format(Date date);按照指定的模式,把Date日期,格式化为符合模式的字符串;
- Date parse(String source); 把符合模式的字符串解析为Date日期
DateFormat类是一个抽象类,无法直接创建对象使用,可以使用DateFormat的子类
java.text.SimpleDateFormat extends DateFormat;
构造方法:
SimpleDateFormat(String pattern) 用给定模式和默认语言环境的日期格式符号构建SimpleDateFormat
参数: String pattern 传递指定的模式
模式: 区分大小写
y | 年 | M | 月 |
---|---|---|---|
d | 日 | H | 时 |
m | 分 | s | 秒 |
“yyyy-MM-dd HH:mm:ss”
“yyyy年MM月dd日 HH时mm分ss秒”
注意事项: 模式中的字母不能更改,连接模式的符号可以改变。
3.4 DateFormat类的farmat方法和parse方法
farmat方法
使用步骤:
- 创建SimpleDateFormat对象,构造方法中传递指定的模式。
- 调用SimpleDateFormat对象中的方法farmat,按照构造方法中指定的模式把Date日期格式化为符合模式的字符串文本。
parse方法
使用步骤:
- 创建SimpleDateFormat对象,构造方法中传递指定的模式;
- 调用SimpleDateFormat对象中的方法parse,把符合构造方法中模式的字符串解析为Date日期。
注意事项:
public Date parse(String source) throws ParseException
- parse方法声明了一个异常叫ParseException解析异常;
- 如果字符串和构造方法中的模式不一样,那么程序就会抛出该异常;
- 调用一个抛出了异常的方法,就必须处理这个异常,要么throws继续声明抛出这一个异常,要么try…catch自己处理这个异常(快捷键Alt+Enter)。
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
public class DemoDateFormat {
public static void main(String[] args) throws ParseException {
demo1();
demo2();
}
public static void demo1() {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
Date date = new Date();
String text = sdf.format(date);
System.out.println(text); //2020-05-09 23:01:40
System.out.println(date); //Sat May 09 23:01:40 CST 2020
}
public static void demo2() throws ParseException {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy年MM月dd日 HH时mm分ss秒");
Date date = sdf.parse("2020年10月11日 10时11分12秒");
System.out.println(date); //Sun Oct 11 10:11:12 CST 2020
}
}
3.5 Calendar类介绍
java.util.Calendar; 是日历类,方便获取各个时间属性的。
- Calendar类是一个抽象类里面提供了很多操作日历字段的方法(YEAR, MONTH, DAY_OF_MONTH, HOUR)。
- Calendar类无法直接创建对象使用,它有一个静态方法getInstance(),该方法返回Calendar类的子类对象。
- static Calendar getInstance(); 使用默认时区和语言环境获得一个日历。
3.6 Calendar类的常用成员方法
- public int get(int field): 返回给定日历字段的值。
- public void set(int field, int value): 将给定的日历字段设置为给定值
- public abstract void add(int field, int amount): 根据日历的规则,为给定的日历段添加或删去指定的时间量。
- public Date getTime(): 返回一个表示Calendar时间值(从历元到现在的毫秒偏移量)的Date对象,把日历转换为日期。
成员方法的参数:
- field:日历段的字段,可以使用Calendar类的静态成员变量获取
- public static final int YEAR = 1; 年
- public static final int MONTH = 2; 月
- public static final int DATE = 5; 月中的某一天
- public static final int DAY_OF_MONTH = 5; 月中的某一天
- public static final int HOUR = 10; 时
- public static final int MINUTE = 12; 分
- public static final int SECOND = 12; 秒
import java.util.Calendar;
import java.util.Date;
public class DemoCalendar {
public static void main(String[] args) {
Calendar c = Calendar.getInstance();
System.out.println(c);
demo1();
demo2();
demo3();
demo4();
}
public static void demo1() {
Calendar c = Calendar.getInstance();
int year = c.get(Calendar.YEAR);
int month = c.get(Calendar.MONTH); //西方的月份0-11
int date = c.get(Calendar.DATE);
System.out.println(year + "," + month + "," + date);
}
public static void demo2() {
Calendar c = Calendar.getInstance();
//将年改为3030年
c.set(Calendar.YEAR, 3030);
//可同时设置年月日
c.set(3030, 3, 3);
int year = c.get(Calendar.YEAR);
int month = c.get(Calendar.MONTH);
int date = c.get(Calendar.DATE);
System.out.println(year + "," + month + "," + date); //3030,3,3
}
public static void demo3() {
Calendar c = Calendar.getInstance();
//增加两年
c.add(Calendar.YEAR, 2);
//减少三月
c.add(Calendar.MONTH, -3);
int year = c.get(Calendar.YEAR);
int month = c.get(Calendar.MONTH);
System.out.println(year + "," + month);
}
public static void demo4() {
Calendar c = Calendar.getInstance();
Date date = c.getTime();
System.out.println(date);
}
}