文章目录
面向对象高级进阶
一、内部类
1.内部类概述
是类中的五大成分之一(成员变量、方法、构造器、内部类、代码块),如果一个类定义在另一个类的内部,这个类就是内部类。
场景:当一个类的内部,包含了一个完整的事物,且这个事物没有必要单独设计时,就可以把这个事物设计成内部类
分类:成员内部类、局部内部类、局部内部类、匿名内部类
创建对象的格式:
外部类名.内部类名 对象名 = new 外部类(...).new 内部类(...); Outer.Inner in = new Outer().new Inner();
2.成员内部类
2.1 概述
- 就是类中的一个普通成员
public class Quter{ //成员内部类 public class Inner{ } }
- 注意:JDK16之前,成员内部类中不能定义静态成员,JDK16开始也可以定义静态成员
2.2 创建对象的格式
外部类名.内部类名 对象名 = new 外部类(...).new 内部类(...)
Quter.Inner in = new Quter().new Inner();
2.3 成员内部类中访问其他成员的特点
- 可以直接访问外部类中的实例成员、静态成员
- 可以拿到当前外部类对象,格式:外部类名.this
public class Demo1 {
public static void main(String[] args) {
A a = new Demo1().new A();
a.show();
}
public class A{
public void show(){
System.out.println("show");
}
}
}
3.静态内部类
3.1 概述
- 有static修饰的内部类,属于外部类持有
public class Quter{
//静态内部类
public static class Inner{
}
}
3.2 创建对象的格式
外部类名.内部类名 对象名 = new 外部类.内部类(...)
Quter.Inner i1 = new Quter.Inner();
3.3 静态内部类中访问外部类成员的特点
- 可以直接访问外部类的静态成员,不可以访问外部类的实力成员
public class Demo {
public static void main(String[] args) {
// Outer o = new Outer();
// Outer.Inner i1 = o.new Inner();
Outer.Inner i1 = new Outer.Inner();
System.out.println(i1.name);
i1.show();
}
}
4.局部内部类
- 局部内部类是定义在在方法中、代码块中、构造器等执行体中
public class Test {
public static void main(String[] args) {
}
public static void go(){
class A{
}
abstract class B{
}
interface C{
}
}
}
5.匿名内部类
5.1 概述
就是一种特殊的局部内部类;所谓匿名:指的是程序员不需要为这个类声明名字
//创建一个Animal的匿名内部子类对象 new Animal(){ @Override public void cry() {...} };
5.2 特点
- 匿名内部类本质就是一个子类,并会立即创建出一个子类对象
5.3 作用
用于更方便的创建一个子类对象
Inter i1 = new Inter() { @Override public void method() { System.out.println("接口"); } }; i1.method();
5.4 使用场景
- 通常作为一个参数传输给方法
二、枚举
1.枚举类本质
就是一个只能对外提供指定个数对象的类
2.枚举类格式
修饰符 enum 枚举类名{
名称1 , 名称2, ... ;
其他成员…
}
3.枚举类注意点
枚举类中的第一行,只能写一些合法的标识符(名称),多个名称用逗号隔开。
这些名称,本质是常量,每个常量都会记住枚举类的一个对象。
4.枚举类特点
public enum A{
X , Y , Z;
}
//上面的代码等价于:
public final class A extends java.lang.Enum<A> {
public static final A X = new A();
public static final A Y = new A();
public static final A Z = new A();
public static A[] values();
public static A valueOf(java.lang.String);
}
枚举类的第一行只能罗列一些名称,这些名称都是常量,并且每个常量记住的都是枚举类的一个对象。
枚举类的构造器都是私有的(写不写都只能是私有的),因此,枚举类对外不能创建对象。
枚举都是最终类,不可以被继承。
枚举类中,从第二行开始,可以定义类的其他各种成员。
编译器为枚举类新增了几个方法,并且枚举类都是继承:java.lang.Enum类的,从enum类也会继承到一些方法
三、泛型
1.泛型概述
- 定义类、接口、方法时,同时声明了一个或者多个类型变量(如:) ,称为泛型类、泛型接口,泛型方法、它们统称为泛型。
- 作用:泛型提供了在编译阶段约束所能操作的数据类型,并自动进行检查的能力!这样可以避免强制类型转换,及其可能出现的异常
- 本质:把具体的数据类型作为参数传给类型变量
2.泛型类
修饰符 class 类名<类型变量,类型变量, ...> {
}
在【创建对象】的时候才能确定创建类的 <类型>
注意:类型变量建议用大写英文字母,常用的有:E、T、K、V等
3.泛型接口
修饰符 interface 类名<类型变量,类型变量, ...> {
}
方式一:指定接口的 <具体泛型>
public class 实现类 implements 接口<具体泛型>{ }
public class InterImpl implements Inter<String>{
public void show(String s){ }
}
方式二:在创建-[泛型实现类]-对象的时候传入 <泛型>
public class 实现类<泛型> implements 接口<泛型>{ }
public class InterImpl<T> implements Inter<T>{
public void show(T t){ }
}
4.泛型方法
修饰符 <类型变量,类型变量,…> 返回值类型 方法名(形参列表) {
}
public static <T> void test(T t){
}
一般情况下,会把静态方法定义为泛型方法
格式:
public static<T> 返回值类型 方法名(参数列表){ }
5.泛型上、下限
5.1 通配符
就是 “?” ,可以在“使用泛型”的时候代表一切类型; E T K V 是在定义泛型的时候使用
5.2 泛型上、下限
泛型上限: ? extends Car: ? 能接收的必须是Car或者其子类 。
-- 可以传递指定类的对象或者子类对象
泛型下限: ? super Car : ? 能接收的必须是Car或者其父类。
-- 可以传递指定类或者父类对象
6.泛型的擦除问题和注意事项
泛型是工作在编译阶段的,一旦程序编译成class文件,class文件中就不存在泛型了,这就是泛型擦除。
泛型不支持基本数据类型,只能支持对象类型(引用数据类型)。
四、进阶:常用API
1.API概述
- API(Application Programming interface):应用程序接口
- 就是别人帮我们已经写好的程序,如:类、方法等,拿过来直接用
2.Object类[超类]
Object类是Java中所有类的祖宗类,因此,Java中所有类的对象都可以直接使用Object类中提供的一些方法
2.1 Object常用方法
方法名 | 说明 |
---|---|
public String toString() | 返回对象的字符串表示形式 |
public boolean equals(Object o) | 判断两个对象是否相等[地址值] |
protected Object clone() | 对象克隆 |
2.2 Object clone()克隆
- clone()浅克隆
- 拷贝出的新对象,与原对象中的数据一模一样(引用类型拷贝的只是地址)
- clone()深克隆
- 对象中基本类型的数据直接拷贝
- 对象中的字符串数据拷贝的还是地址
- 对象中还包含的其他对象,不会拷贝地址,会创建新对象。
package d4_api_object;
import java.util.Objects;
public class Student implements Cloneable{
private String name;
private int age;
String[] hobby;
public String[] getHobby() {
return hobby;
}
public void setHobby(String[] hobby) {
this.hobby = hobby;
}
public Student() {
}
//重写 toString() 方法,默认的 toString() 方法输出的是 随机值
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
//重写equal方法,比较两个对象的每个属性值 是否相容,返回值是 boolean类型
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof Student)) return false;//判断两个值类型是否一致
Student student = (Student) o;
return age == student.age && Objects.equals(name, student.name);
}
@Override
public int hashCode() {
return Objects.hash(name, age);
}
//重写 clone() 方法,要想重写 clone() 方法,本类必须作为实现类去实现 Cloneable接口
@Override
public Object clone() throws CloneNotSupportedException {
return super.clone();
}
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 Student(String name, int age) {
this.name = name;
this.age = age;
}
}
//测试类
package d4_api_object;
public class Demo {
public static void main(String[] args) throws CloneNotSupportedException {
Student s1 = new Student("张三", 16);
System.out.println(s1);
Student s2 = new Student("张三", 16);
System.out.println(s1.equals(s2));
System.out.println("==========");
Object s3 = s1.clone();
System.out.println(s1 == s3);
System.out.println("==========");
String[] arr = s1.getHobby();
}
}
3.Objects
3.1 Objects概述
- 是一个工具类,提供了很多操作对象的静态方法给我们使用
3.2 Objects类常见方法
方法名 | 说明 |
---|---|
public static boolean equals(Object a, Object b) | 先做非空判断,再比较两个对象 (防止了空指针异常) |
public static boolean isNull(Object obj) | 判断对象是否为 null,为null返回 true |
public static boolean nonNull(Object obj) | 判断对象是否不为null,不为null则返回true |
import java.util.Objects;
public class Demo1 {
public static void main(String[] args) {
String s1 = null;
String s2 = "张三";
// System.out.println(s1.equals(s2));报错,空指针异常
System.out.println("=============");
System.out.println(Objects.equals(s1,s2));
System.out.println(Objects.isNull(s1));
}
}
4.包装类
- 包装类就是把基本的数据包装成对象
4.1 基本数据类型及其包装类
基本数据类型 | 对应的包装类(引用数据类型) |
---|---|
byte | Byte |
short | Short |
int | Integer |
long | Long |
char | Character |
float | Float |
double | Double |
boolean | Boolean |
4.2 包装类的装箱和拆箱
- 装箱:将基础类型的数据包装成封装类的对象
- 拆箱:将包装类对象拆分为基础类型的数据
public class IntTest {
public static void main(String[] args) {
//装箱:将基础类型的数据包装成封装类的对象
Integer i = Integer.valueOf(10);
//拆箱:将包装类对象拆分为基础类型的数据
int i2 = i.intValue();
-- 自动装箱
Integer i3 = 10; // <==> Integer i3 = Integer.valueof(10);
-- 自动拆箱
int i4 = i3 + 12;// <==> int i4 = i3.intValue() + 12;
Integer i5 = i3 + 13;
/*
* 这行代码执行步骤:
* 1.自动拆箱: i3.intValue()
* 2.加: i3.intValue() + 13
* 3.自动装箱: Integer i5 = Integer.valueof(i3.intValue() + 13)
*/
}
}
- String和int互转
public class IntToString {
public static void main(String[] args) {
//将int转换为 String
//方式一:拼接空字符串
int a = 10;
String s1 = a + "";
//方式二:toString()
Integer i2 = a;
String s2 = i2.toString();
//方式三:valueOf()
String s3 = String.valueOf(10);
//将String转换为 int
int i4 = Integer.parseInt("10");
//注意
int i5 = Integer.parseInt("a");//报错:NumberFormatException: For input string: "a"
//Character 包装类没有 pareXxx()方法
}
}
5.StringBuilder、StringBuffer
5.1 StringBuilder
- 代表可变字符串对象,相当于是一个容器,它里面装的字符串是可以改变的,就是用来操作字符串的
好处:StringBuilder比String更适合做字符串的修改操作,效率会更高,代码也会更简洁
方法:
构造器 | 说明 |
---|---|
public StringBuilder() | 创建一个空白的字符串对象,不包含任何内容 |
public StringBuilder(String str) | 创建一个指定字符串内容的可变字符串对象 |
方法名称 | 说明 |
---|---|
public StringBuilder append(任意类型) | 添加数据并返回StringBuilder对象本身 |
public StringBuilder reverse() | 将对象的内容反转 |
public int length() | 返回内容对象长度 |
public StringBuilder toString() | 通过toString()将StringBuilder转换为String |
注意点:
- 对于字符串相关的操作,如频繁的拼接、修改等,建议用StringBuidler,效率更高!
- 注意:如果操作字符串较少,或者不需要操作,以及定义字符串变量,还是建议用String
5.2 StringBuffer
- 用法和StringBuilder一样
5.3 两者区别
StringBuffer是线程安全的,效率低;StringBuilder是线程不安全的,效率高
既然 StringBuffer是线程安全的,它的所有公开方法都是同步的,StringBuilder 是没有对方法加锁同步的,所以毫无疑问,StringBuilder的性能要远大于StringBuffer
// “zhangsan”反转并转换为 String 输出
System.out.println((new StringBuilder("zhangsan").reverse()).toString());
// abccba 判断回文:
System.out.println((new StringBuilder("abccba").reverse()).toString().equals("abccba"));
为什么对于字符串相关的操作,如频繁的拼接、修改等,建议用StringBuidler
String 是不可变字符串、频繁操作字符串会产生很多无用对象,性能差。
StringBuilder:是内容可变的字符串、拼接字符串性能好、代码优雅。
注意:如果操作字符串较少,或者不需要操作,以及定义字符串时,则用String
6.StringJoiner
6.1 StringJoiner概述
- JDK8开始才有的,跟StringBuilder一样,也是用来操作字符串的,也可以看成是一个容器,创建之后里面的内容是可变的
好处:不仅能提高字符串的操作效率,并且在有些场景下使用它操作字符串,代码会更简洁
6.2 构造器、方法
构造器 | 说明 |
---|---|
public StringJoiner(间隔符号) | 创建一个StringJoiner对象,指定拼接时的间隔符号 |
public StringJoiner(间隔符号,开始符号,结束符号) | 创建一个StringJoniner对象,指定拼接时的间隔符号、开始符号、结束符号 |
方法名称 | 说明 |
---|---|
public StringJoiner add(添加的内容) | 添加数据,并返回对象本身 |
public int length() | 返回长度(字符出现的个数) |
public String toString() | 返回一个字符串(拼接之后的结果) |
import java.util.StringJoiner;
public class JoinerDemo {
public static void main(String[] args) {
int[] arr = {11,22,33,44};
System.out.println(arrToString(arr));
}
public static String arrToString(int[] arr){
//StringJoiner(间隔符号,开始符号,结束符号)
StringJoiner joiner = new StringJoiner(",", "[", "]");
for (int i = 0; i < arr.length; i++) {
joiner.add(arr[i]+"");
}
System.out.println(joiner.length())// 输出 13
return joiner.toString();
}
}