定义类在成员位置
- 成员内部类(没有static修饰)
- 静态内部类(使用static修饰)
定义类在局部位置
- 局部内部类(有类名)
- 匿名内部类(没有类名!!!)
概念:在一个类的内部再定义一个完整的类。
特点:
- 编译后可生成独立的字节码文件。
- 内部类可直接访问外部类的私有成员而不破坏封装。
- 最大特点可直接访问私有属性;
类的五大成员
属性、方法、构造器、代码块、内部类。
成员内部类- 在类的内部定义,与实例变量、实例方法同级别的类。
- 外部类的一个实例部分,创建内部类对象时,必须依赖外部类对象。
- Outer out =new Outer();
- Outer.Inner in = out.new Inner(); //Outer.Inner in = new Outer().new Inner()
class Outer{
class Inner{
}
}
//Outer$Inner.class Outer.class
成员内部类的使用
- 可以直接访问外部类的所有成员,包括私有的;
- 可以添加任意修饰符。因为它的地位就是一个成员;
- 作用域和外部类的其他成员一样;
- 成员内部类------>访问------->外部类成员:访问方式: 直接访问
- 外部类------>访问---------->成员内部类:访问方式: 创建对象,再访问
- 如果外部类和内部类的成员重名时,内部类访问的话,默认遵循就近原则,如果内部类想访问外部类成员,则可以使用**(外部类名.this.成员)**去访问。
- 不依赖外部类对象,可直接创建或通过类名访问,可声明静态成员;
- 只能直接访问外部类的静态成员(实例成员需实例化外部类对象)
- Outer.Inner inner = new Outer.inner();
- Outer.Inner.show();//Outer类中包含静态Inner类,静态Inner类中包含静态show()方法;
class Outer{
static class Inner{
}
}
静态内部类的使用
- 可以直访问外部类的所有静态成员包括私有的,但不能直接访问非静态成员
- 如果外部类和静态内部类的成员重名时遵循就近原则,如果想访问外部类的成员,则可以使用**(new外部类名() .成员)**去访问。
- 定义在外部类方法中,作用范围和创建对象范围仅限于当前方法。
- 局部内部类访问外部类的当前方法的局部变量时,因无法保障变量的生命周期与自身相同,变量必须修饰为final
- 限制类的使用范围。
class Outer02 {//外部类
private int n1 = 100;
private void m2() {
System.out.println("Outer02 m2()");
}
public void m1() {
final class Inner02 {
private int n1 = 800;
public void f1() {
System.out.println("n1=" + n1 + " 外部类的 n1=" + Outer02.this.n1); System.out.println("Outer02.this hashcode=" + Outer02.this);
m2();
}
}// 外部类在方法中,可以创建 Inner02 对象,然后调用方法即可
Inner02 inner02 = new Inner02();
inner02.f1();
}
}
局部内部类的使用
- 可以直接访问外部类的所有成员,包括私有的;
- 不能添加访问修饰符,因为它的地位就是一个局部变量,局部变量不能使用修饰符。但是可以用final修饰,因为局部变量也可以使用final;
- 作用域:仅仅在定义它的方法或代码块中;
- 局部内部类----->访问----->外部类的成员 访问方式:直接访问
- 外部类---->访问----->局部内部类成员 访问方式:创建对象,再访问(必须在作用域中)
- 如果外部类和局部内部类的成员重名时,默认遵循就近原则,如果想访问外部类的成员可以使用(外部类名.this.成员)去访问;
- 没有类名的局部内部类(一切特征都与局部内部类相同)
- 必须继承一个父类或实现一个接口。
- 定义类、实现类、创建对象的语法合并,只能创建一个该类的对象。
- 优点:减少代码量
- 缺点:可读性差
public class Outer04 {
public static void main(String[] args) {
Outer03 outer03 = new Outer03();
outer03.method();
}
}
class Outer03{
private int n1 = 10;
public void method(){
IA tiger = new IA() {
@Override
public void cry() {
System.out.println("tiger eating...");
}
};
tiger.cry();
Father father = new Father(){
@Override
public void test() {
// super.test();
System.out.println("匿名内部类重写父类方法");
}
};
father.test();
Animal animal = new Animal() {
@Override
void eat() {
System.out.println("animal eating");
}
};
animal.eat();
}
}
interface IA{
public void cry();
}
class Father{
public void test(){};
}
abstract class Animal{
abstract void eat();
}
- 匿名内部类—访问---->外部类成员访问方式:直接访问
- 外部其他类—不能访问----->匿名内部类(因为匿名内部类地位是一个局部变量)
- 如果外部类和匿名内部类的成员重名时,匿名内部类访问的话,默认遵循就近原则如果想访问外部类的成员,则可以使用**(外部类名.this.成员)**去访问
匿名内部类最佳实践
当做实参直接传递,简洁高效。
public class InnerClass {
public static void main(String[] args) {
//传统方法
f(new Picture());
//当做实参直接传递
f(new Li() {
@Override
public void show() {
System.out.println("匿名内部类做实参的展示");
}
});
}
public static void f(Li li){
li.show();
}
}
interface Li{
void show();
}
class Picture implements Li{
@Override
public void show() {
System.out.println("实现接口的名画展示");
}
}
Object类
- 超类、基类,所有类的直接或间接父类,位于继承树的的最顶层
- 任何类,如没有书写extends显示继承某个类,都默认继承Object类,否则为间接继承
- Object类中所定义的方法,是所有对象都具备的方法。
- Object类型可以存储任何对象
- 作为参数,可以接受任何对象。
- 作为返回值,可返回任何对象。
- public final Class> getClass(){}
- 返回引用中存储的实际对象类型。
- 通常用于判断两个引用中实际存储对象类型是否一致。
- public int hashCode(){}
- 返回该对象的十进制的哈希码值
- hash算法根据对象的地址或字符串或数字计算出来的int类型的数值。
- 哈希码并不唯一,可保证相同对象返回相同哈希值,尽量保证不同对象返回不同哈希码。
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 {}
toString()方法
- public String toString(){}
- 返回该对象的字符串表示(表现形式)
- 可以根据程序需求覆盖该方法,如:展示对象各个属性值。
- public boolean equals(Object obj){}
- 默认实现为(this == obj),比较两个对象地址是否相同
- 可进行覆盖,比较两个对象的内容是否相同。
==和equals的区别
答:==可以判断基本类型,又可以判断引用类型;如果判断基本类型,判断的是值是否相等,如果判断引用类型,判断的是地址是否相等,即判定是不是同一个对象;equals是Object类中的方法,只能判断引用类型。默认判断的是地址是否相等,子类中往往重写该方法,用于判断内容是否相等,比如Integer String
重写equals方法
应用实例: 判断两个 Person 对象的内容是否相等,如果两个 Person 对象的各个属性值都一样,则返回 true,反之 false。
public class TestEquals {
public static void main(String[] args) {
Person person1 = new Person(2, "lihua");
Person person2 = new Person(2, "lihua2");
System.out.println(person1.equals(person2));
}
}
class Person{
private int age;
private String name;
public Person(int age,String name){
this.age = age;
this.name = name;
}
public boolean equals(Object obj){
if(this == obj){
return true;
}
//类型判断
if(obj instanceof Person){//是Person才比较
Person p = (Person)obj;//向下转型获得person的各个属性
return this.name.equals(p.name)&&this.age == p.age;
}
//如果不是Person直接返回FALSE
return false;
}
}
finalize()方法
实际情况用的少,面试中大多会问
- 当对象被判定位垃圾对象时,由JVM自动调用此方法,用以标记垃圾对象,进入回收队列。
- 垃圾对象:没有有效引用指向此对象时,为垃圾对象
- 垃圾回收:由GC销毁垃圾对象,释放数据存储空间
- 自动回收机制:JVM的内存耗尽,一次性回收所有垃圾对象
- 手动回收机制:使用System.gc();通知JVM执行垃圾回收
public class Finalize_ {
public static void main(String[] args) {
Car bmw = new Car("宝马");
//这时 car 对象就是一个垃圾,垃圾回收器就会回收(销毁)对象, 在销毁对象前,会调用该对象的 finalize 方法
//,程序员就可以在 finalize 中,写自己的业务逻辑代码(比如释放资源:数据库连接,或者打开文件..)
//,如果程序员不重写 finalize,那么就会调用 Object 类的 finalize, 即默认处理
//,如果程序员重写了 finalize, 就可以实现自己的逻辑
bmw = null;
System.gc();//主动调用垃圾回收器
System.out.println("程序退出了....");
}
}
class Car {
private String name;
//属性, 资源。。
public Car(String name) {
this.name = name;
}
//重写 finalize
@Override
protected void finalize() throws Throwable {
System.out.println("我们销毁 汽车" + name );
System.out.println("释放了某些资源...");
}
}
面试题
GC是什么? 为什么要有GC?
答:Gc是垃圾收集的意思(Gabage Collection),内存处理是编程人员容易出现的问题的地方,忘记或者错误的内存回收会导致程序或系统的不稳定甚至崩溃,java提供的GC功能可以自动检测对象是否超过作用域从而达到自动回收内存的目的,java语言没有提供释放已分配内存的显示操作方法。
垃圾回收的优点和原理。并考虑2种回收机制
答:Java语言中一个显著的特点就是引入了垃圾回收机制,使c++程序员最头疼的内存管理的问题迎刃而解,它使得Java程序员在编写程序的时候不再需要考虑内存管理。由于有个垃圾回收机制,Java中的对象不再有"作用域"的概念,只有对象的引用才有"作用域"。垃圾回收可以有效的防止内存泄露,有效的使用可以使用的内存。垃圾回收器通常是作为一个单独的低级别的线程运行,不可预知的情况下对内存堆中已经死亡的或者长时间没有使用的对象进行清楚和回收,程序员不能实时的调用垃圾回收器对某个对象或所有对象进行垃圾回收。回收机制有分代复制垃圾回收和标记垃圾回收,增量垃圾回收。
垃圾回收的基本原理是什么?垃圾回收器可以马上回收内存吗有什么办法主动通知虚拟机进行垃圾回收?
答:对于GC来说,当程序员创建对象时,GC就开始监控这个对象的地址、大小以及使用情况。通常,GC采用有向图的方式记录和管理堆(heap)中的所有对象。通过这种方式确定哪些对象是“可达的”,哪些对象是“不可达的”。当GC确定一些对象为“不可达”时,GC就有责任回收这些内存空间。可以。程序员可以手动执行System.gc(),通知GC运行,但是Java语言规范并不保证GC一定会执行。



