Object类
Object类的概述
Object类是所有对象的直接或者间接父类,传说中的上帝。该类中定义的肯定是所有对象都具备的功能。
Object类中的equals方法
Object类中已经提供了对对象是否相同的比较方法,如果自定义类中也有比较相同的功能,没有必要重新定义。只要沿袭父类中的功能,建立自己特有比较内容即可,这就是覆盖。例,假如有一个Person类,其代码为:
class Person extends Object
{
private int age;
private String name;
Person(String name, int age)
{
this.name = name;
this.age = age;
}
}
现在我们的需求是定义一个方法,判断两个Person对象是否是同一个,判断的依据是根据姓名和年龄,如果姓名和年龄都相同,视为同一个人。我们可以这样分析,不用再自定义方法判断对象是否相同了,因为在Object父类中,已经定义了这样的方法,直接使用就可以了,但是判断的内容是根据Person类的特点定义的,那就需要保留父类的功能声明,定义子类功能的特有内容,使用覆盖。所以要在Person类中加入如下equals方法:
public boolean equals(Object obj) // Object obj = p2;--->Object obj = new Person();
{
// 提高点效率。如果两个引用指向了同一个对象,就不用再转换并比较内容了,直接判断地址就哦了。
if (this == obj)
return true;
// obj.age是错误的,因为Object中没有age属性,
// 想要使用子类对象的特有属性或行为,必须对其进行向下转型,并且需要进行类型判断
if (!(obj instanceof Person))
{
// return false;
throw new ClassCastException("类型错误");
}
Person p = (Person)obj;
// 如果判断姓名字符串是否相同,不要用==,字符串本身是一个对象,所以要使用String类的equals方法
return this.name.equals(p.name) && this.age == p.age;
}
Object类中的toString方法
有时候,我们还需要重写Object类的toString()方法,建立Person对象特有的字符串表现形式。查询API帮助文档,我们可以发现:Object类的toString方法返回的是一个字符串,该字符串由类名(对象是该类的一个实例)、at标记符“@”和此对象哈希码的无符号十六进制表示组成。换句话说,该方法返回一个字符串,它的值等于:getClass().getName() + '@' + Integer.toHexString(hashCode())
。如果我们不在Person类中重写该方法,运行以下程序:
class ObjectDemo
{
public static void main(String[] args)
{
Person p1 = new Person("lisi", 21);
Person p2 = new Person("mazi", 25);
System.out.println(p1); // Person@139a55,打印对象时,默认调用toString()方法
System.out.println(p1.toString());
}
}
则会输出Person@139a55,而且在打印对象时,默认调用了toString()方法。顺便说一下,如果要我们自己来弄,输出Person@139a55这样的东西,又该怎么做呢?这时,我们就要接触一点点反射的知识了,关于反射的知识以后会另开一篇专讲。A.class、B.class这些class文件都有名称,这些文件内都有构造函数、一般方法,Java中用Class来描述这些class文件,可通过getName()获取名称。所以以下代码会输出Person。
Person p1 = new Person("lisi", 21);
Class c = p1.getClass();
System.out.println(c.getName()); // Person
现在我们就可以自己来编写了,代码如下:
Person p1 = new Person("lisi", 21);
Class c = p1.getClass();
System.out.println(c.getName()+"@@"+Integer.toHexString(p1.hashCode()));
这时就会输出Person@@139a55这样的东西了。
说完toString()方法,现在我们就要在Person类中重写该方法了,所以应在Person类中添加如下toString()方法:
/**
建立Person对象特有的字符串表现形式,只要覆盖toString方法即可
*/
public String toString()
{
return "Person[name = " + this.name +", age = " + this.age + "]";
}
内部类
内部类的概述
将一个类定义在另一个类的里面,对里面的那个类就称为内部类(内置类,嵌套类)。例如,A类要直接访问B类中的成员时,可以将A类直接定义到B类中,作为B类的内部类存在。
内部类的访问规则
内部类可以直接访问外部类中的成员,包括私有。之所以可以直接访问外部类中的成员,是因为内部类中持有了一个外部类中的引用,格式为外部类名.this。用一个例子来验证:
class Outer
{
int num = 2;
class Inner
{
int num = 3;
void show()
{
int num = 4;
System.out.println("show..." + Outer.this.num);
}
}
public void method()
{
new Inner().show();
}
}
class InnerClassDemo2
{
public static void main(String[] args)
{
new Outer().method();
}
}
而外部类要想访问内部类,那么只能创建内部类的对象来访问。
非静态,非私有的内部类访问方式
当内部类定义在外部类的成员位置上,而且非私有时,在外部其他类中,可以直接建立内部类对象,格式为:
外部类名.内部类名 变量名 = 外部类对象.内部类对象;
例子如下:
class Outer
{
private int num = 4;
class Inner // 内部类,相当于外部类中的一个成员,它就可以被成员修饰符所修饰,public private static
{
void show()
{
System.out.println(num);
}
}
}
class InnerClassDemo
{
public static void main(String[] args)
{
Outer.Inner in = new Outer().new Inner();
in.show();
}
}
而且在非静态内部类中只允许定义静态的常量,存于常量池中,不能定义其他静态成员。所以,以下代码编译是没有任何问题的:
class Outer
{
private int num = 4;
class Inner // 内部类,相当于外部类中的一个成员,它就可以被成员修饰符所修饰,public private static
{
static final int count = 5; // 在非静态内部类中只允许定义静态的常量,存于常量池中,不能定义其他静态成员
void show()
{
System.out.println(num);
}
}
}
class InnerClassDemo
{
public static void main(String[] args)
{
Outer.Inner in = new Outer().new Inner();
in.show();
}
}
静态,非私有的内部类访问方式,访问非静态成员
当内部类在成员位置上时,就可以被成员修饰符修饰,比如,private将内部类在外部类中进行封装,static内部类就具备static的特性。当内部类被static修饰后,只能直接访问外部类中的static成员,出现了访问局限。那么在外部其他类中,如何直接访问静态内部类中的非静态成员呢?访问格式为new Outer.Inner().function();
。例,
class Outer
{
private static int num = 4;
class Inner // 内部类,相当于外部类中的一个成员,它就可以被成员修饰符所修饰,public private static
{
void show()
{
System.out.println(num);
}
}
static class Inner2 // 静态内部类,相当于一个外部类
{
void show2()
{
System.out.println("show2..." + num);
}
}
}
class InnerClassDemo
{
public static void main(String[] args)
{
Outer.Inner2 in = new Outer.Inner2();
in.show2();
}
}
静态,非私有的内部类访问方式,访问静态成员
在外部其他类中,如何直接访问静态内部类中的静态成员呢?访问格式为Outer.Inner.function();
。例,
class Outer
{
private static int num = 4;
class Inner // 内部类,相当于外部类中的一个成员,它就可以被成员修饰符所修饰,public private static
{
void show()
{
System.out.println(num);
}
}
static class Inner2 // 静态内部类,相当于一个外部类
{
void show2()
{
System.out.println("show2..." + num);
}
static void show3()
{
System.out.println("show3..." + num);
}
}
}
class InnerClassDemo
{
public static void main(String[] args)
{
Outer.Inner2.show3();
}
}
从上述程序代码可以看出,静态内部类里面甭管是静态方法还是非静态方法,只能访问外部类中的静态成员。
注意:当内部类中定义了静态成员,该内部类必须是static的。当外部类中的静态方法访问内部类时,内部类也必须是static的。
class Outer {
private static int x = 3;
static class Inner { // 静态内部类
static void function() { // 当内部类中定义了静态成员,该内部类必须是static的
System.out.println("inner::::"+x); // 当内部类被static修饰后,只能直接访问外部类中的static成员
}
}
static class Inner2 {
void show() {
System.out.println("inner2 show");
}
}
public static void method() {
new Inner2().show(); // 当外部类中的静态方法访问内部类时,内部类也必须是static的
}
}
内部类定义在局部时
- 不可以被成员修饰符修饰,因为private、static不能修饰局部成员;
可以直接访问外部类中的成员,因为还持有外部类中的引用。但是不可以访问它所在的局部中的变量,只能访问被final修饰的局部变量,主要原因是生命周期不同。注意:JDK1.8没这个区别了,但是被final修饰的变量是一个常量,只能被赋值一次,所以一经存在就不得更改。
为了能说明局部内部类只能访问被final修饰的局部变量,而且其主要原因是生命周期不同这一点,我们举例验证。class Outer { private int num = 4; Object obj; public void method() { /*final*/ int x = 5; class Inner extends Object // Inner本身继承Object { // 覆盖Object类中的toString()方法 public String toString() { System.out.println("x = " + x); System.out.println("show..." + num); return "Inner...abc"; } } // 创建内部类的对象 Inner in = new Inner(); // 将内部类对象的地址赋值给obj obj = in; } public void function() { // 打印obj指向的对象的字符串表现形式 System.out.println(obj.toString()); } } class InnerClassDemo2 { public static void main(String[] args) { new Outer().method(); } }
以上程序代码在JVM内存中的体现大概是这样的:
匿名内部类
匿名内部类的概述
匿名内部类其实就是内部类的简写格式。定义匿名内部类有一个前提:内部类必须是继承一个类或者实现接口。匿名内部类定义的格式为:
new 父类或者接口() {定义子类的内容};
从上述格式可以看出,匿名内部类其实就是一个匿名子类对象,而且这个对象有点胖,可以理解为带内容的对象。还有一点需要注意的是匿名内部类中定义的方法最后不要超过3个。
abstract class AbsDemo {
abstract void show();
}
class Outer {
int x = 3;
public void function() {
AbsDemo d = new AbsDemo() {
int num = 9;
void show() {
System.out.println("num==="+num);
}
void abc() {
System.out.println("haha");
}
};
d.show();
// d.abc(); // 编译失败,因为父类中没有这个方法
}
}
匿名内部类的练习
练习一,补全代码,通过匿名内部类。
interface Inter {
void method();
}
class Test {
// 补足代码。通过匿名内部类
}
class InnerClassTest {
public static void main(String[] args) {
Test.function().method();
}
}
通过匿名内部类补全代码后:
interface Inter {
void method();
}
class Test {
// 补足代码。通过匿名内部类
static Inter function() {
return new Inter() {
public void method() {
System.out.println("Inter method");
}
};
}
}
class InnerClassTest {
public static void main(String[] args) {
// Test.function():Test类中有一个静态的方法function
// .method():function这个方法运算后的结果是一个对象,而且是一个Inter类型的对象,
// 因为只有是Inter类型的对象,才可以调用method()
Test.function().method();
}
}
练习二,如果没有一个类继承或一个接口实现,还能使用匿名内部类吗?答案是可以的。
class InnerTest {
public static void main(String[] args) {
new Object() { // new Object() {}是Object类的子类对象
public void function() {
System.out.println("hello");
}
}.function();
}
}
这是面试时可能遇到的一个小问题哟!