内部类
内部类:指的是一个事物当中包含另一个事物,也就是一个类包含里另一个类;
分类:
1.成员内部类
2.局部内部类(包含匿名内部类)
3.静态内部类
4.匿名内部类
成员内部类的格式:
修饰符 class 类名称{
修饰符 class 类名称{
//…
}
//…
}
注意:内用外,随意访问 外用内,一定要内部类对象
使用外部内部类的两种方式:
1.间接方法:在外部类的方法当中,使用内部类、main只是调用外部类的方法。
2.直接方法:
类名称 对象名=new 类名称();
【外部类名称.内部类名称 对象名=new 外部类名称().new 内部类名称;】
局部内部类:如果一个类定义在一个方法内部,那么这就是一个局部内部类。
局部内部类定义格式:
修饰符 class 外部类名称{
修饰符 返回值类型 外部类方法名(参数列表){
class 局部内部类名称{
//方法体…
}
}
}
定义一个类的时候,权限修饰符规则:
1.外部类:public/(default)
2.成员内部类:public/protected/(default)/private
3.局部内部类:什么都不写
局部内部类,如果希望访问所在的局部变量,那么局部变量必须是【有效final】
匿名内部类
如果接口的实现类(或者是父类的子类)只需使用唯一的一次。
那么这种情况下就可以省略掉该类的定义,而改为使用【匿名内部类】
匿名内部类的格式:
接口名 对象名=new 接口名(){
//覆盖所有抽象方法
};
注意事项:
1.匿名内部类在创建对象时,只能使用唯一的一次;
2.匿名内部类【调用方法】,只能调用唯一一次;
3.匿名内部类是省略了【实现类/子类名称】,但是匿名对象是省略了【对象名称】
1.成员内部类
成员内部类不允许存在任何static变量或静态方法 (成员方法与对象相关、静态属性与类有关)
1、成员内部类是依赖于外部类的对象而存在的,在创建成员内部类的实例时,外部类的实例必须已经存在。
2、成员内部类可以直接访问外部类的所有属性和方法。
3、创建内部类实例的方法:
1)在外部类中创建:内部类名 name = this.new 内部类名();
2)在非外部类中创建:外部类名.内部类名 name = new 外部类名().new
内部类名();
成员内部类的特征:
1.成员内部类可以访问外部类的静态与非静态的方法和成员变量。
2.尽管这些类成员被修饰为private.内部类的实例一定要绑定在外部类对象上。
3.对于非静态内部类,不能有静态成员,例如变量,方法等。
注意:成员内部类中不能有静态属性和方法但可以有静态常量(默认为static final修饰的属性),因为在类加载时会初始化static修饰的内容,而成员内部类必须依赖于内部类对象,即成员内部类要在外部类加载之后加载,所以不能包含静态的变量和方法。使用final修饰的静态变量类加载时会自动初始化其值,将其作为一个常量,所以可以出现在内部类中。
/**
*
* 成员内部类:定义类的内部方法外部
* 内部类:一个类中包含另外一个类
* 特点:内部类可以生成独立的class文件;可访问外部的属性;还可以时实现外部类的 功能
*
*/
class Outter{
private String name="郑爽";
public int add(int a,int b){
return new Inner().addFun(a,b);
}
class Inner{
private int age=18;
private String name="杨幂";
public int addFun(int a,int b){
System.out.println("内部类属性name:"+name); //默认为this.name
System.out.println("外部类属性name:"+Outter.this.name);
System.out.println("内部类属性name:"+this.name);
System.out.println("内部类属性name:"+age);
return a+b;
}
}
}
public class TestInnerClass {
public static void main(String[] args) {
//内部类的对象的创建需依赖外部类的实例
Outter.Inner inner=new Outter().new Inner();
System.out.println("内部类中addFun的和为:"+inner.addFun(99,900));
}
}
2.局部内部类
范围:定义在外部类的方法当中的类,称为局部内部类。
作用域:仅限于当前方法当中。
注意:局部内部类中不可定义静态变量,可以访问外部类的局部变量(即方法内的变量),但是变量必须是final的。
局部内部类的使用:
只能在当前方法当中使用,外部若想使用内部类只能依赖外部类的方法。
/**
* 局部内部类:
* 定义在外部类方法体中的类;不能使用public修饰
*/
class Outter02{
private String name="小明";
public void show(){
final String address="广州"; //当使用局部变量时,将默认添加final
//局部内部类,只能在方法体中使用
class Inner02{
private String name="老王";
public void innerShow(){
System.out.println("局部类属性name:"+name);
System.out.println("外内部类属性name:"+Outter02.this.name);
System.out.println("局部变量:"+address);
System.out.println("局部内部类........");
}
}
//局部类必须实例对象外部才能在外部使用
new Inner02().innerShow();
}
}
public class PartInnerClass {
public static void main(String[] args) {
Outter02 outter02=new Outter02();
outter02.show();
}
}
注意:
在类外不可直接使用局部内部类(保证局部内部类对外是不可见的)。要想使用局部内部类时需要实例对象,对象调用方法,在方法中才能调用其局部内部类。
3. 静态内部类
范围:定义在类当中且被static修饰的类,称为静态内部类。
静态内部类的特征:
1、属性和方法可以声明为静态的或者非静态的。
2、实例化静态内部类:比如:B是A的静态内部类,A.B b = new A.B();
3、内部类只能引用外部类的静态的属性或者方法。
4、如果属性或者方法声明为静态的,那么可以直接通过类名直接使用。比如B 是A的静态内部类,b()是B中的一个静态属性,则可以:A.B.b();
/**
* 静态内部类
*/
class Outter01{
private String name="小张";
private static String address1="云南";
private static int age=18;
static class Inner01{
private String name="小刘";
private String address2="广西";
public void show(){
//System.out.println(name); //内部类不能访问外部类的
System.out.println("内部类访问外部类的静态属性address1:"+address1); //内部类能访问外部类的静态属性
System.out.println("内部类访问内部类的静态属性address2:"+address2);
System.out.println("内部类访问内部类的非静态属性name:"+name);
System.out.println("静态内部类的调用.......");
}
}
}
public class StaticInnerClass {
public static void main(String[] args) {
Outter01.Inner01 inner01=new Outter01.Inner01();
inner01.show();
}
}
4.匿名内部类
定义在外部类中且没有名字的类,其本质为多态,称为匿名内部类;
匿名内部类的特点:
1)匿名内部类不能定义任何静态成员、方法;
2)匿名内部类中的方法不能为抽象的;
3)匿名内部类必须实现接口或抽象父类的所有抽象方法;
4)匿名内部类访问的外部类成员变量或成员方法必须用static修饰;
前提:必须先存在一个类或者一个接口(该类可以是抽象类,也可以是具体类)
格式:new + 类名或者接口名() + {重写方法;} //前面是创建一个对象,后面是定义一个内部类。
本质:创建了一个继承该类或者实现该接口的子类的匿名对象 ;
在使用匿名内部类时,前提是必须存在继承或者接口,同时也只能继承一个类或者实现一个接口。在只需实例一次对象时,通常使用匿名内部类的方式。
案例1:
/**
* 匿名内部类:
* 没有类名的局部内部类,本质为多态
*/
// 打电话
interface Call{
public abstract void call();
}
class Person implements Call{
@Override
public void call() {
System.out.println("老王正在打电话........接口多态方式");
}
}
public class AnonymityInnerClass {
public static void main(String[] args) {
//接口实现多态
Call call=new Person();
call.call();
System.out.println("-----------------------------------");
//匿名内部类实现
Call call01 =new Call() {
@Override
public void call() {
System.out.println("小张正在打电话........匿名内部类方式");
}
};
call01.call();
System.out.println("-----------------------------------");
//匿名内部类的回调:接口作为形参
callback(new Call() {
@Override
public void call() {
System.out.println("小阳正在打电话........匿名内部类回调");
}
});
}
//回调方式的静态方法
private static void callback(Call call){
call.call();
}
}
案例2:
/**
* 使用匿名内部类的方式测试执行效率
*/
//测试遍历for循环执行效率
interface Timeable{
public abstract void testTime();
}
//测试工具
class Tool{
public static long getTime(Timeable timeable){ //接口回调,接口作为参数
long start=System.currentTimeMillis();
timeable.testTime();
long end=System.currentTimeMillis();
return end-start;
}
}
public class GetTime {
public static void main(String[] args) {
//匿名内部类实现
long time = Tool.getTime(new Timeable() {
@Override
public void testTime() {
//打印10000次
for (int i = 0; i < 10; i++) {
System.out.print(i+" ");
}
}
});
System.out.println(" ");
System.out.println("for循环的执行效率为:"+time+"毫秒");
}
Object类
Object类是所有类的父类,每个类都能调用Object中的方法,每个类如没有继承父类则默认继承Object类。
案例:
/**
* object中的成员方法
*/
class Student{
String name;
int age;
public Student(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public int hashCode(){
return name.hashCode()+age;
}
@Override
public String toString(){
return "name:"+name+"-----age:"+age;
}
}
public class object {
public static void main(String[] args) {
Student student01=new Student("李海龙",24);
Student student02=new Student("刘大东",24);
System.out.println("===========getClass获取类对象=============");
Class aClass = student01.getClass();
Class aClass1 = student02.getClass();
System.out.println(aClass==aClass1);
String string = new String("黄涛");
Class aClass2 = string.getClass();
System.out.println(aClass1==aClass2);
System.out.println("============hashcode值============");
System.out.println(student01.hashCode());
System.out.println(student02.hashCode());
System.out.println(student02.hashCode());
System.out.println("============toString方法========== ");
System.out.println(student01.toString());
System.out.println(student02.toString());
System.out.println(student01); //打印对象,默认重写toString方法
}
}
成员方法:
1.getClass():
public final Class<?> getClass()返回的是Object运行时的类对象;
实际结果类型是 Class<? extends |X|>,其中 |X| 表示清除表达式中的静态类型,该表达式调用 getClass。 例如,以下代码片段中不需要强制转换:
Number n = 0;
Class<? extends Number> c = n.getClass();
注意事项:
- 返回的是运行时的类
- 它是用final修饰的,所以任何一个对象使用该方法都是返回运行时类,不能被重写;
2.toString():
public String toString():该方法返回对象的字符串;
Object的默认实现:
public String toString() {
return getClass().getName() + “@” + Integer.toHexString(hashCode());
}
3.重写hashCode():
public int hashCode():返回该对象的哈希码值;
重写hashCode()方法需要遵循hashCode()规范:
1.一致性:在Java应用程序执行期间,在对同一对象多次调用hashCode方法时,必须一致地返回相同的整数,前提是将对象进行hashcode比较时所用的信息没有被修改。
2.equals:如果根据equals()方法比较,两个对象是相等的,那么对这两个对象中的每个对象调用hashCode()方法都必须生成相同的整数结果,注:这里说的equals()方法是指Object类中未被子类重写过的equals()方法。
3.附加:如果根据equals()方法比较,两个对象不相等,那么对这两个对象中的任一对象上调用hashCode方法不一定生成不同的整数结果。但是,程序员应该意识到,为不相等的对象生成不同整数结果可以提高哈希表的性能。
4.重写equals():
@Override
public boolean equals(Object object){
//判断是否为当前对象
if (this==object){
return true;
}
//任何对象不等于null,比较是否为同一类型
if (object==null){
return false;
}
//强制类型转换
if (object instanceof Person){
//比较属性值
Person person= (Person) object;
return this.name.equals(person.name)&&this.age==person.age;
}
return false;
}
Object类中equals方法比较的是两个对象的引用地址值;
finalize()方法:
自动清理:
当垃圾回收器确定对象不再使用时,由对象的垃圾回收器JVM调用此方法。子类重写 finalize 方法,以配置系统资源或执行自动清除。
手动清理:调用System.gc()方法手动清理垃圾;