内部类
内部类(innerclasses)是一类特殊的类,指的是定义在一个类的内部的类。实际开发中,为了方便的使用外部类的相关属性和方法,这个时候我们通常会定义一个内部类。
位置:把一个类定义到另一个类中,那么内部的类就是内部类。
注意:内部类不能直接创建。
创建内部类的语法:外部类.内部类 变量名 = new 外部类对象.new内部类对象
内部类的外部类的方法如果想要访问内部类的方法,必须创建内部类的对象,根据内部类的对象来访问。
内部类可以使用public、default、protected、private以及static修饰。而外部顶级类只能使用public和default修饰。
内部类只是一个编译时的概念,一旦我们编译成功,就会成为完全不同的两个类。对于一个名为Outter的外部类和其内部定义的名为Inner的内部类。编译完成后会出现Outter.class和Outter$Inner.class两个类的字节码文件。所以内部类是相对独立的一种存在,其成员变量/方法名可以和外部类的相同。
内部类的编译后的class文件:
内部类的作用
内部类提供了更好的封装。只能让外部类直接访问不允许同一个包中的其他类直接访问。
内部类可以直接访问外部类的私有属性,内部类被当成其外部类的成员。但外部类不能访问内部类的内部属性。
内部类的分类
非静态内部类
非静态内部类,外部类里使用非静态内部类和平时使用其他类没什么不同。
-
非静态内部类对象必须寄存在一个外部类对象里。因此,如果有一个非静态内部类。对象那么一定存在对应的外部类对象。非静态内部类对象单独属于外部类的某个对象。
-
非静态内部类可以直接访问外部类的成员,但是外部类不能直接访问非静态内部类成员。
-
非静态内部类不能有静态方法、静态属性和静态初始化块。
-
成员变量访问要点:
-
内部类里方法的局部变量:变量名。
-
内部类属性:this.变量名。
-
外部类属性:外部类名.this.变量名。
-
class Outter {
int num = 20;
public void outMethod() {
System.out.println("我是外部的类的方法");
}
class Inner {
int innerNum;
int num = 10;
public void inMethod() {
//当外部类和内部类的属性发生重合时,可以使用Outter.this.成员名
System.out.println(Outter.this.num);
}
}
}
public class TestInner {
public static void main(String[] args) {
//创建内部类的对象 外部类.内部类 变量名 = new 外部类对象.new内部类对象
Outter.Inner inner = new Outter().new Inner();
inner.innerNum = 13;
inner.inMethod();//我是内部的类的方法
}
}
静态内部类
静态内部类可以访问外部类的静态成员,不能访问外部类的普通成员。静态内部类看做外部类的一个静态成员。
class Outer {
private int a = 10;
private static int b = 20;
//相当于外部类的一个静态成员
static class Inner {
public void test() {
// System.out.println(a);//静态内部类不能访问外部类的普通属性
System.out.println(b);//静态内部类可以访问外部类的静态属性
}
}
}
public class Test {
public static void main(String[] args) {
//通过 new 外部类名.内部类名() 创建内部类对象
Outer.Inner inner = new Outer.Inner();
inner.test();
}
}
匿名内部类
什么是匿名类:没有名字的类,这种类需要在接口上实现。适合只需要使用一次的类。
匿名类和匿名内部类都需要接口或者抽象类的支持。
创建一个匿名的类的对象这个类的对象实现OuterInter的接口,在大括号中实现接口中的方法,方法调用完毕后就会被垃圾回收。
接口中的匿名内部类
interface Outter {
public void outMethod();
}
class OutterImpl implements Outter {
public void outMethod() {
}
}
public class TestInner {
public static void main(String[] args) {
Outter outter = new OutterImpl();
outter.outMethod();
//创建的不是接口本身,new Outter() 后面的大括号就是一个匿名内部类,
// 实现了Outter接口
Outter o = new Outter() {
@Override
public void outMethod() {
System.out.println("我是匿名内部类的方法");
}
};
o.outMethod();
}
//我是匿名内部类的方法
}
抽象类中的匿名内部类
abstract class Outter{
public abstract void outMethod();
}
public class TestInner{
public static void main(String[] args){
//创建的不是接口本身,new Outter() 后面的大括号就是一个匿名内部类,
// 实现了Outter接口
Outter o = new Outter() {
@Override
public void outMethod() {
System.out.println("我是匿名内部类的方法");
}
};
o.outMethod();
}
//我是匿名内部类的方法
}
局部内部类
定义在方法内部的,作用域只限于本方法,称为局部内部类。
局部内部类的的使用主要是用来解决比较复杂的问题,想创建一个类来辅助我们的解决方案,到那时又不希望这个类是公共可用的,所以就产生了局部内部类。局部内部类和成员内部类一样被编译,只是它的作用域发生了改变,它只能在该方法中被使用,出了该方法就会失效。局部内部类在实际开发中应用很少。
//方法中的内部类
public class Test {
public void show() {
//作用域仅限于该方法
class inner {
public void fun() {
System.out.println("局部内部类");
}
}
new inner().fun();
}
public static void main(String[] args) {
new Test().show();
}
}
Object
Object概述
Object是所有类的根类,所有的类都是直接或者间接的去继承Object类。
类 Object 是类层次结构的根类。每个类都使用 Object 作为超类。所有对象(包括数组)都实现这个类的方法。
在定义一个类时,没有使用extends关键字,也就是没有显式地继承某个类,那么这个类直接继承Object类。所有的对象都继承这个类的方法。
基本方法
toString():返回当前对象本身的有关信息,返回字符串对象。通常,toString() 方法会返回一个“以文本方式表示”此对象的字符串。结果应是一个简明但易于读懂的信息表达式。建议所有子类都重写此方法。
Object中的toString()的实现:
Object 类的 toString 方法返回一个字符串,该字符串由类名(对象是该类的一个实例)、at 标记符“@”和此对象哈希码的无符号十六进制表示组成。换句话说,该方法返回一个字符串,它的值等于: 获得类的名称“@”,getClass().getName() + '@' + Integer.toHexString(hashCode())
。
public class Test{
public static void main(String[] args) {
Object o = new Object();
System.out.println(o.toString());
}
}//java.lang.Object@1540e19d
public class Test{
private String name = "张三";
private int age = 10;
@Override
public String toString() {
return "name='" + name + '\'' + ", age=" + age ;
}
public static void main(String[] args) {
Test t=new Test();
System.out.println(t.toString());
}
}//name='张三', age=10
getClass():getClass()返回值是Class<T>
(类的类对象),与反射有关。通过getClass().getName()
获得对象的类名。
hashCode():由 Object 类定义的 hashCode() 方法确实会针对不同的对象返回不同的整数。
finalize():用于垃圾回收,我们不用手动的去调用,由JVM来调用,当垃圾回收器确定不存在对该对象的更多引用时,由对象的垃圾回收器调用此方法。
**equals(Object obj),返回值为布尔类型,**用于比较两个对象是否相等。
public class Test {
public static void main(String[] args) {
//1. 创建Object类型的对象
Object obj1 = new Object();
Object obj2 = new Object();
//2. 测试Object类中的成员方法
//2.1 hashCode:不同对象的哈希码值一般不同
int code1 = obj1.hashCode();
int code2 = obj2.hashCode();
System.out.println(code1);//356573597
System.out.println(code2);//1735600054
//2.2 Class<?> getClass():一个类只有一个字节码对象。
Class cls1 = obj1.getClass();
Class cls2 = obj2.getClass();
System.out.println(cls1);//class java.lang.Object
System.out.println(cls2);//class java.lang.Object
//2.3 String toString():返回对象的字符串形式,默认返回的是地址值,不同的对象有不同的返回值
//地址值的组成:全类名@该对象的哈希码的无符号十六进制形式
String s1 = obj1.toString();
String s2 = obj2.toString();
System.out.println(s1);//java.lang.Object@1540e19d
System.out.println(s2);//java.lang.Object@677327b6
//2.4 boolean equals():比较两个对象是否相等,默认比较地址值(无意义),子类一般会重写。
boolean b = obj1.equals(obj2);
System.out.println(b);//false
}
}
对象的克隆
将一个对象复制一份,称为对象的克隆技术。
在object类中存在一个clone()方法:protected Object clone() throws CloneNotSupportedException
如果某个类的对象要想被克隆,则对象所在的类必须实现Cloneable接口。此接口没有定义任何方法,是一个标记接口。
对象克隆的应用场景是需要创建一组细粒度重复使用的对象时,使用克隆方式可能比直接创建对象的效率要高得多。
public class User implements Cloneable {
private String name;
private Integer age;
//省略构造方法()、getter()、setter()、toString()
//重写Object中的clone()方法
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
public class Test {
public static void main(String[] args) {
User user = new User("Tom",20);
try {
User clone = (User) user.clone();
System.out.println(user);
System.out.println(clone);
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
}
}
评论区