内部类
「概念」 :在一个类内部再定义一个完整的类。
一般情况下类与类之间是相互独立的,内部类的意思就是打破这种独立思想,让一个类成为另一个类的内部信息,和成员变量、成员方法同等级别。
「内部类的好处:」
把一个类写在外面和写在里面最终达到的结果都一样,那我们为什么还要使用内部类,岂不是多此一举吗?
「采用内部类这种技术,可以隐藏细节和内部结构,封装性更好,让程序的结构更加合理!如果类很多且都暴露在外面,那么类与类之间的调用就会十分繁琐!」
内部类的分类:
1.成员内部类(非静态内部类)
参考代码如下:
package NeiBuLei; public class OuterClass { //成员变量 private String OuterName; //成员方法 public void display(){ System.out.println("这是外部类方法!"); System.out.println(OuterName); } //内部类 public class InnerClass{ //成员变量 private String InnerNme; //构造方法 public InnerClass() { InnerNme = "Inner Class"; } //成员方法 public void display(){ System.out.println("这是内部类方法!"); System.out.println(InnerNme); } } // 主方法 public static void main(String[] args) { OuterClass outerClass = new OuterClass(); outerClass.display();//这是外部类方法!null // 这个类是内部类,已经不是独立的类了,因此不能像外部类一样直接创建! //InnerClass innerClass = new InnerClass(); 行不通 OuterClass.InnerClass innerClass = outerClass.new InnerClass();// 同成员方法/变量 只是加了个前缀 innerClass.display();// 这是内部类方法! } }
输出结果:
这是外部类方法!null 这是内部类方法!Inner Class
「总结:成员内部类(非静态内部类)的使用就是将内部类作为外部类的的一个成员变量/成员方法来使用,所以必须依赖于外部类的对象才能调用,用法和成员变量/成员方法一致!」
2.局部内部类
局部内部类:基本的内部类还可以在一个方法体中定义。
package NeiBuLei; public class OuterClass { //成员变量 private String OuterName; //成员方法 public void display(){ class InnerClass { public void print(){ System.out.println("这是一个局部内部类方法!"); } } InnerClass innerClass = new InnerClass(); innerClass.print(); } // 主方法 public static void main(String[] args) { OuterClass outerClass = new OuterClass(); outerClass.display(); } }
静态内部类
静态内部类的构造不需要依赖于外部类对象,类中的静态组件都不需要依赖于任何对象,可以直接通过「类本身」 进行构造。
package NeiBuLei; public class OuterClass { //成员变量 private String OuterName; //成员方法 public void display(){ System.out.println("这是外部类方法!"); System.out.println(OuterName); } //静态内部类 public static class InnerClass{ private String InnerName; public InnerClass() { InnerName = "Inner Class"; } //成员方法 public void display(){ System.out.println("这是静态内部类方法!"); System.out.println(InnerName); } } // 主方法 public static void main(String[] args) { OuterClass outerClass = new OuterClass(); outerClass.display(); // 静态内部类的构造不依赖与外部类,可以直接通过类本身进行构造! InnerClass innerClass = new InnerClass(); innerClass.display(); } }
输出结果:
这是外部类方法!null 这是静态内部类方法!Inner Class
4.匿名内部类
匿名内部类:没有名字的内部类。
匿名内部类「主要应用与接口的实现!」
接口:
package NeiBuLei; public interface MyInterface { public void test(); }
实现类:
package NeiBuLei; public class MyImplement implements MyInterface{ @Override public void test() { System.out.println("test"); } }
匿名内部类的使用:
package NeiBuLei; public class MyImplement implements MyInterface{ @Override public void test() { System.out.println("test"); } }
「匿名内部类的好处:」
我们定义接口之后,「它的实现类不需要去单独创建一个文件去写它的实现」 ,我们可以把这个实现类的操作写到我们调用的地方就可以了!写起来更加简洁、方便。
「匿名内部类的缺点:」
耦合度太高了!
Object类
匿名内部类的缺点
Object类常用方法:
1.equals方法
「==与equals的对比【面试题】+ jdk查看原码」
==是一个比较运算符
==:既可以判断基本类型,又可以判断引用类型
==:如果判断的是「基本类型」 ,「判断的是值是否相等」 。
//==: 如果判断的是基本类型,判断的是 值 是否相等 int x1 = 10; int x2 = 10; double x3 = 10.0; System.out.println(x1 == x2);//true System.out.println(x1 == x3);//true
==:如果判断的是「引用类型」 ,「判断的是地址是否相等,即判断是不是同一个对象」
package Equals; public class Test01 { public static void main(String[] args) { //==: 如果判断的是引用类型,判断的是地址是否相等,即判断是不是同一个对象 A a = new A(); A b = a; A c = b; System.out.println(a==c);// ? true System.out.println(b==c);// true B obj = a; System.out.println(obj==c);// true } } class B{} class A extends B{}
equals方法是Object类中的方法,「只能判断引用类型」 。
❝ idea查看Jdk原码:鼠标光标放在要查看的方法上,直接输入ctrl + b 查看某个类所有方法:ctrl + F12 ❞
默认判断的是地址是否相等,「子类(Object类是所有类的父类)往往重写该方法,用于判断内容是否相等」 。
/* Object类 equals()方法原码 //默认判断地址是否一样 public boolean equals(Object obj) { return (this == obj); }
子类往往重写该方法,用于判断内容是否相等 String类中的equals()方法原码(重写了父类equals()方法)
public boolean equals(Object anObject) { if (this == anObject) { // 如果是同一个对象(地址相同) return true; // 返回true } if (anObject instanceof String) { // 判断类型 String anotherString = (String)anObject; // 向下转型 int n = value.length; if (n == anotherString.value.length) { // 如果长度相同 char v1[] = value; char v2[] = anotherString.value; int i = 0; while (n-- != 0) { // 比较每一个字符 if (v1[i] != v2[i]) return false; i++; } return true; // 如果两个字符串每一个字符都相同,则返回true } } return false; } */
在看个例子
【小练习】
写出输出结果:
package Equals; public class EqualsTest01 { public static void main(String[] args) { Person p1 = new Person(); p1.name = "tom"; Person p2 = new Person(); p2.name = "tom"; System.out.println(p1 == p2);// 引用类型——判断是否为同一个对象(地址) System.out.println(p1.name.equals(p2.name));// p.name是String类型,重写了equals()方法——判断内容是否一样 System.out.println(p1.equals(p2));//p1,p2属于Person类,该类并没有重写equals()方法(继承父类equals()方法,即判断地址) String s1 = new String("abc"); String s2 = new String("abc"); System.out.println(s1.equals(s2)); System.out.println(s1 == s2); } } class Person{ public String name; }
输出结果:
false true false true false
2.hashCode方法
小结:(可以当作地址来看但它本质上不是地址)
提高具有哈希结构的容器的效率
两个引用,如果指向的是同一个对象,则哈希值肯定一样
两个引用,如果指向的是不同对象,则哈希值是不一样的
哈希值主要根据地址号来!不能将哈希值完全等价于地址
在后面的集合中hashCode如果需要的话,也会重写
package hashCode; public class HashCode { public static void main(String[] args) { AA aa = new AA(); AA aa2 = new AA(); AA aa3 = aa; System.out.println("aa.hashCode()="+ aa.hashCode()); System.out.println("aa2.hashCode()="+ aa2.hashCode()); System.out.println("aa3.hashCode()="+ aa3.hashCode()); } } class AA{}
输出结果:
❝ aa.hashCode()=460141958 aa2.hashCode()=1163157884 aa3.hashCode()=460141958 ❞
3.toString方法
toString方法
基本介绍:
默认返回:全类名 + @ + 哈希值的十六进制
/* Object toString()原码 //(1)getClass().getName() 类的全类名(包名+类名) //(2)Integer.toHexString(hashCode()) 将hashCode的值转成16进制字符串 public String toString() { return getClass().getName() + "@" + Integer.toHexString(hashCode()); } */
「子类往往重写toString方法,用于返回对象的属性信息(快捷键:alt + insert),当然我们也可以自己定制。」
当我们输出一个对象时,toString()方法会被默认调用
4.finzlize方法
finzlize方法:当垃圾收集确定不再有对该对象的引用时,垃圾收集器在对象上调用该对象。
当对象被回收时,系统自动调用该对象的finzlize方法。子类可以重写该方法,做一些释放资源的操作
什么时候被回收:当某个对象没有任何引用时,则jvm就认为这个对象是一个垃圾对象,就会时候垃圾回收机制来销毁该对象,在销毁该对象前,会先调用finzlize方法。
垃圾回收机制的调用,是由系统来决定(即有自己的GC算法),也可以通过System.gc()主动触发垃圾回收机制。
❝ 注:在实际开发中,几乎不会用finzlize方法,更多的是为了应付面试 ❞