什么是面向对象
- 面向对象编程 (OOP, Object-Oriented Programming)
- 面向对象的本质就是以类的方式组织代码,以对象的方式组织(封装)数据
- 抽象
- 三大特性
类与对象的关系
创建与初始化对象
构造器详解
创建对象的内存分析
package com.hunter.oop;
public class Pet {
public String name;
public int age;
public void shout() {
System.out.println("叫了一声");
}
}
package com.hunter.oop;
public class Application {
public static void main(String[] args) {
Pet dog = new Pet();
dog.name = "旺财";
dog.age = 3;
dog.shout();
System.out.println(dog.name);
System.out.println(dog.age);
Pet cat = new Pet();
}
}

Java内存分析
## 封装
继承
继承的本质是对某一批类的抽象,从而实现对现实世界更好的建模。
子类是父类的扩展(extends)
- 子类继承了父类,就会拥有父类的全部方法和属性(private除外)
- 被
final
修饰的类无法被继承
继承是类和类之间的一种关系,此外还有依赖、组合、聚合等
Object类
在Java中,所有的类都默认直接或间接继承Object类。
Java类只有单继承,没有多继承
一个子类只能有一个父类
super
super和this的比较
this表示本身调用者这个对象
super表示父类对象,只能在继承后使用
构造方法
this()
:本类的构造方法,可以简化代码的编写
super()
:父类的构造方法
重写 Override
方法的重载
重写需要有继承关系,是子类对父类非静态的方法进行重写,与属性无关。
多态
多态就是同一方法可以根据发送对象的不同而采用多种不同的行为方式
一个对象的实际类型是确定的,但指向的引用类型可以不确定。
Student s1 = new Student();
Person s2 = new Student();
Object s3 = new Student();
s2.run();
((Student) s2).eat();
一个对象的实际类型是确定的,但可以指向对象的引用的类型有很多
多态存在的条件
- 有继承关系
- 子类重写了父类方法
- 父类引用指向子类对象
Father f = new Son();
多态是方法的多态,属性没有多态
- 静态方法(static)的调用只和左边定义的数据类型有关
- 常量(final)的调用也只和左边定义的数据类型有关
- private方法的调用也只和左边定义的数据类型有关
关键词 instanceof
关键词instanceof
用于判断一个对象是什么类型
Object object = new Student();
System.out.println(object instanceof Student);
System.out.println(object instanceof Person);
System.out.println(object instanceof Object);
System.out.println(object instanceof Teacher);
System.out.println(object instanceof String);
Person person = new Student();
System.out.println(person instanceof Student);
System.out.println(person instanceof Person);
System.out.println(person instanceof Object);
System.out.println(person instanceof Teacher);
System.out.println(X instanceof Y);
语句能否编译通过要看X与Y之间是否有继承关系。
引用类型的类型转换
Person obj = new Student();
((Student) obj).go()
- 引用类型的类型转换可能会丢失自己本来的一些方法:
- 子类转换为父类是向上转型
- 父类转换为子类为向下转型,属于强制转换
- 引用类型的类型转换是为了方便方法的调用,减少代码冗余
static关键字详解
代码块
publica class ClassName {
{
}
static {
}
}
静态代码块
加载类时就执行(只执行一次)
匿名代码块
- 创建对象时自动创建,且在构造器之前创建
- 运用场景:赋一些初始值
静态导入包
为了方便,想不加类名来使用工具类的静态方法,需要通过静态(static)导入包来实现:
import static java.lang.Math.random;
public class Application {
public static void main(String[] args) {
System.out.println(random());
}
}
抽象类
abstract
修饰符可以修饰方法,也可以修饰类。
如果修饰方法,方法就是抽象方法;如果修饰类,类就是抽象类。
抽象类中可以没有抽象方法,但有抽象方法的类一定要声明为抽象类
抽象类中可以有普通的方法
抽象类不能用new
关键字来创建对象,它是用来让子类继承的
抽象类不能被创建对象,但有构造器。那么构造方法的作用是什么?
抽象类的构造方法可以用来初始化抽象类内部声明的通用变量,并被各种实现使用。
抽象方法
只有方法的声明,没有方法的实现,实现交由子类来实现。
子类继承抽象类,就必须要实现抽象类没有实现的抽象方法,否则该子类也要声明为抽象类
接口
普通类:只有具体实现
抽象类:具体实现和**规范(抽象方法)**都有
接口:只有规范,自己无法写方法。使约束和实现分离,面向接口编程。
声明接口的关键字是interface
接口就是规范,定义的是一组规则,让不同的人实现。
OO(Object-Oriented)的精髓就是对对象的抽象,最能体现这一点的就是接口。
设计模式所研究的,实际上就是如何合理地去抽象。
接口中定义属性和方法
属性是常量 public static final
,但一般不会在接口中定义属性;方法是抽象的public abstract
。因此接口中定义的属性和方法的修饰符可以省略。
public interface UserService {
// 接口中的所有定义的方法其实都是抽象的 public abstract,因此修饰符可省略
void run();
}
- **类只能单继承,但接口可以多继承(可以实现多个接口)**
- 类通过关键字`implements`**实现接口**。**实现了接口的类,必须重写接口中的方法**。
- **实现接口的类的命名**通常以`Impl`结尾。
```java
// 类可以通过implements实现多个接口
public class UserServiceImpl implements UserService, TimeService {
@Override
public void add(String name) {
}
@Override
public void timer() {
}
}
接口中没有构造方法(抽象类有构造方法)
内部类
成员内部类
public class Outer {
private int id = 10;
public void out() {
System.out.println("这是外部类的方法");
}
public class Inner {
public void in() {
System.out.println("这是内部类的方法");
}
public void getID() {
System.out.println(id);
}
}
}
public class Application {
public static void main(String[] args) {
Outer outer = new Outer();
Outer.Inner inner = outer.new Inner();
inner.in();
inner.getID();
}
}
静态内部类
public class Outer {
private int id = 10;
public void out() {
System.out.println("这是外部类的方法");
}
public static class Inner {
public void in() {
System.out.println("这是内部类的方法");
}
}
}
局部内部类
局部内部类位于方法中
public class Outer {
public void method() {
class Inner {
public void in() {
}
}
}
}
匿名内部类
没有类的名称,必须借助接口或者父类。
public class Test {
public static void main(String[] args) {
new Apple().eat();
}
UserService userService = new UserService() {
@Override
public void hello() {
}
}
}
class Apple {
public void eat() {
System.out.println("1");
}
}
interface UserService {
void hello();
}
常用API
Object 类
概述
java.lang.Object
类是Java语言中所有类的父类,其中描述的所有方法,子类都可以使用。如果一个类没有特别指定父类,则默认继承自Object类。
toString 方法
直接打印对象的变量名,其实就是调用toString方法(对象在堆内存中的地址值)。但是Object默认的toString方法获取对象的地址值其实没有多少意义和可读性。
自定义的类,一般会重写toString方法,获取对象的属性。
equals 方法
默认地址比较
Object默认的equals方法为:使用==
运算符比较对象地址,只要不是同一个对象,结果必然为false。
对象内容比较
如果希望进行对象的内容比较,则可以重写equals方法。equals方法隐含着一个多态(传入的是Object类的对象):
public boolean equals(Object obj) {...}
- 可选:
- 添加一个判断,传递的参数obj如果是this本身,直接返回true,提高程序效率
- 添加一个判断,传递的参数obj如果是null,直接返回false,提高程序效率
- 添加一个判断,防止类型转换异常ClassCastException
- 使用强制类型转换,把obj类转换成需要的类型
- 再比较两个对象的属性
@override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass())
return false;
当前类 x = (当前类) o;
return Objects.equals(this.strAttribute, o.strAttribute) && this.intAttribute == o.intAttribute;
}
clone 方法
clone方法可以实现对象的复制。clone
方法要求子类实现java.lang.Clonable
接口,从而在子类中实现对象的复制。
浅克隆:对象在复制时仅复制基本类型的属性值到新对象中,引用的变量不会被复制。
深度克隆:不仅复制基本类型的属性值到新对象中,引用的变量本身也会被复制。
实现深度克隆
要实现深度克隆,需要重写clone方法。
Objects 类
JDK 1.7添加了一个java.util.Objects
工具类,它提供了一些方法来操作对象,这些方法是**null-save(空指针安全)**的,用于计算对象的hashcode、返回对象的字符串表示形式、比较两个对象。
在比较两个对象的时候,Object的equals方法容易抛出空指针异常,而Objects类中的equals方法就优化了这个问题:
public static boolean equals(Object a, Object b) {
return (a == b) || (a != null && a.euqals(b));
}
日期时间类
Date 类
java.util.Date
类表示特定的瞬间,精确到毫秒。
- 构造方法:
public Date()
public Date(long date)
:分配Date对象并初始化此对象,以表示自从标准基准时间(1970年1月1日00:00:00 GMT)以来的指定毫秒数。
简单来说,使用无参构造,可以自动设置当前系统时间的毫秒时刻,指定long类型可以自定义毫秒时刻;输出Date类时,会自动转换为日期格式。
java.text.DateFormat
是日期/时间格式化子类的抽象类,可以通过这个类完成日期和文本之间的转换,即在Date对象和String对象之间来回转换。
构造方法
由于DateFormat为抽象类,因此需要常用的子类java.text.SimpleDateFormat
。这个类需要指定格式化或解析的格式。
构造方法为:public SimpleDateFormat(String pattern)
。
参数pattern是一个字符串,代表日期时间的自定义格式,常用的格式规则如下:
标识字母(区分大小写) |
含义 |
y |
年 |
M |
月 |
d |
日 |
H |
时 |
m |
分 |
s |
秒 |
格式中的字母不能更改,但连接的符号可以改变。
SimpleDateFormat sdf = new SimpleDateFormat("yyyy年MM月dd日 HH时mm分ss秒");
格式化
调用SimpleDateFormat对象中的format方法,按照构造方法中指定的模式,把Date日期格式化为符合模式的字符串
Date date = new Date();
String d = sdf.format(date);
System.out.println(d);
解析
调用SimpleDateFormat对象中的parse方法,把符合构造方法中的格式的字符串解析为Date对象。
注意:parse方法声明了一个ParseExceptoion,如果字符串和构造方法的格式不一样,就会抛出异常。
Date date = sdf.parse("2021/07/31 22:26:33");
System.out.println(date);
Calendar 类
java.util.Calendar
在Date类之后出现,是一个抽象类,替换掉了很多Date的方法。该类将所有可能用到的时间信息封装为静态成员变量,方便获取。
字段值 |
含义 |
YEAR |
年 |
MONTH |
月(0-11) |
WEEK_OF_YEAR |
一年中的第几周 |
WEEK_OF_MONTH |
一个月中的第几周 |
DATE DAY_OF_MONTH |
日 |
DAY_OF_WEEK |
一周中的第几天 |
DAY_OF_YEAR |
一年中的第几天 |
HOUR |
时 |
MINUTE |
分 |
SECOND |
秒 |
MILLISECOND |
毫秒 |
获取方法
Calendar类无法直接创建对象使用,它有一个静态方法getInstance()
,该方法使用默认时区和语言环境返回Calendar类的子类对象。
常用方法
public int get(int field)
:返回给定日历字段的值(参数使用Calendar类的静态成员变量)
public void set(int field, int value)
:将给定的日历字段设置为给定值
public void set(int year, int month, int day)
:同时设置年月日
public abstract void add(int field, int amount)
:根据日历的规则,为给定的日历字段添加或减去指定的时间量
public Date getTime()
:返回一个表示此Calendar时间值(从历元到现在的毫秒偏移量)的Date对象
Calendar c = new Calendar.getInstance();
int year = c.get(Calender.YEAR);
System.out.println(year);
Math 类
BigInteger 类
BigDecimal 类
System 类
java.lang.system
类中提供了大量的静态方法,可以获取与系统相关的信息或系统级操作,在System类的API文档中,常用的方法有:
public static long currentTimeMills()
:返回以毫秒为单位的当前时间(和DATE类能达到相同的效果),可以用来测试程序的效率
public static void arraycopy(Object src, int srcPos, Object dest, int destPos, int length)
:将数组中指定的数据拷贝到另一个数组中
- src 源数组
- srcPos 起始位置
- dest 目标数组
- destPos 目标数组中的起始位置
- length 要复制的数组长度
currentTimeMills 方法
long s = System.currentTimeMills();
for (int i= 0; i < 10000; i++) {
System.out.println(i);
}
long e = System.currentTimeMills();
System.out.println("程序共耗时:" + (e - s) + "毫秒");
arraycopy 方法
int[] src = {1, 2, 3, 4, 5};
int[] dest = {6, 7, 8, 9, 10};
System.arraycopy(src, 0, dest, 0, 3);
System.out.println(Arrays.toString(dest));
StringBuilder 类
又称为字符串缓冲区,可以提高字符串的操作效率(看作一个长度可变的字符串)。底层是一个初始长度为16的数组,但是没有被final修饰,因此长度可变。
构造方法
public StringBuilder()
:构造一个空的StringBuilder容器
public StringBuilder(String str)
:构造一个StringBuilder容器,并将字符串添加进去
常用方法