继承性的理解
为描述和处理 个人信息,定义类Person:
public class Person {
public String name;
public int age;
public Date date;
}
为描述和处理 学生信息,定义类Student:
public class Student {
public String name;
public int age;
public Date date;
public String school;
}
通过继承,简化Student类的定义:
public class Student extends Person{
public String school;
}
为什么要有继承?
多个类中存在相同属性和行为时,将这些内容抽取到单独一个类中,
那么多个类无需再定义这些属性和行为,只要继承那个类即可。
此处的多个类称为 子类( 派生类),单独的这个类称为父类(基类
或超类)。可以理解为:“子类 is a 父类”
类继承语法规则:
class Subclass extends SuperClass{ }
作用:
继承的出现减少了代码冗余,提高了代码的复用性。
继承的出现,更有利于功能的扩展。
继承的出现让类与类之间产生了关系,提供了多态的前提。
注意:不要仅为了获取其他类中某个功能而去继承
子类继承了父类,就继承了父类的方法和属性。
在子类中,可以使用父类中定义的方法和属性,也可以创建新的数据和
方法。
在Java 中,继承的关键字用的是“extends”,即子类不是父类的子集,
而是对父类的“扩展” 。
关于继承的规则:
的 子类不能直接访问父类中私有的(private)
继承性的使用
子类继承了父类,就继承了父类的方法和属性。
在子类中,可以使用父类中定义的方法和属性,也可以创建新的数据和
方法。
在Java 中,继承的关键字用的是“extends”,即子类不是父类的子集,
而是对父类的“扩展” 。
关于继承的规则:
的 子类不能直接访问父类中私有的(private)
继承性的再说明
Java只能单继承,不可以多继承
方法的重写(override/overwrite)
定义:在子类中可以根据需要对从父类中继承来的方法进行改造,也称
为方法的重置、覆盖。在程序执行时,子类的方法将覆盖父类的方法。
要求:
- 子类重写的方法必须和父类被重写的方法具有相同的方法名称、参数列表
- 子类重写的方法的返回值类型不能大于父类被重写的方法的返回值类型
- 子类重写的方法使用的访问权限不能小于父类被重写的方法的访问权限
子类不能重写父类中声明为private权限的方法 - 子类方法抛出的异常不能大于父类被重写方法的异常
注意:
子类与父类中同名同参数的方法必须同时声明为非static的(即为重写),或者同时声明为
static的(不是重写)。因为static方法是属于类的,子类无法覆盖父类的方法。
关键字 — super
在Java类中使用super来调用父类中的指定操作:
super可用于访问父类中定义的属性
super可用于调用父类中定义的成员方法
super可用于在子类构造器中调用父类的构造器
注意:
尤其当子父类出现同名成员时,可以用super表明调用的是父类中的成员
super的追溯不仅限于直接父类
super和this的用法相像,this代表本类对象的引用,super代表父类的内存
空间的标识
调用父类的构造 器
子类中所有的构造器 默认都会访问父类中 空参数的构造器
当父类中没有空参数的构造器时,子类的构造器必须通过this(参 参
表 数列表)或者super( 参数列表)语句指定调用本类或者父类中相应的
构造器。同时,只能”二选一”,且必须放在构造器的首行
如果子类构造器中既未显式调用父类或本类的构造器,且父类中又
没有无参的构造器,则编译出错
Object 类的理解
Object类是所有Java类的祖先。每个类都使用 Object 作为超类。所有对象(包括数组)都实现这个类的方法。
在不明确给出超类的情况下,Java会自动把Object作为要定义类的超类。
可以使用类型为Object的变量指向任意类型的对象。
Object类有一个默认构造方法pubilc Object(),在构造子类实例时,都会先调用这个默认构造方法。
Object类的变量只能用作各种值的通用持有者。要对他们进行任何专门的操作,都需要知道它们的原始类型并进行 @类型转换 百度百科。
例如:
Object obj = new MyObject();
MyObject x = (MyObject)obj;
多态性的使用
多态性,是面向对象中最重要的概念,在Java中的体现:
对象 的 多态性:父类的引用指向子类的对象
可以直接应用在抽象类和接口上
Java引用变量有两个类型: 编译时类型和 运行时类型。编译时类型由声明
该变量时使用的类型决定,运行时类型由实际赋给该变量的对象决定。简
称:编译时,看左边;运行时,看右边。
若编译时类型和运行时类型不一致 , 就出现了对象的多态性 (Polymorphism)
多态情况下 , “ 看左边 ” : 看的是父类的引用(父类中不具备子类特有的方法)
“ 看右边 ” : 看的是子类的对象(实际运行的是子类重写父类的方法)
对象的多态 —在Java中,子类的对象可以替代父类的对象使用
一个变量只能有一种确定的数据类型
一个引用类型变量可能指向(引用)多种不同类型的对象
Person p = new Student();
Object o = new Person();//Object类型的变量o,指向Person类型的对象
o = new Student(); //Object类型的变量o,指向Student类型的对象
子类可看做是特殊的父类 , 所以父类类型的引用可以指向子类的对象:向上转型(upcasting)
一个引用类型变量如果声明为父类的类型,但实际引用的是子类
对象,那么该变量就 不能 再访问子类中添加的属性和方法
Student m = new Student();
m.school = "pku"; // 合法,Student 类有school 成员变量
Person e = new Student();
e.school = "pku"; // 非法,Person 类没有school 成员变量
属性是在编译时确定的,编译时e 为Person 类型,没有school 成员变量,因而编译错误。
虚拟方法调用(Virtual Method Invocation)
正常的方法调用
Person e = new Person();
e.getInfo();
Student e = new Student();
e.getInfo();
虚拟方法调用( 多态情况下 下)
子 类中定义了与父类同名同参数的方法,在多态情况下,将此时父类的方法称为虚拟方法,父类根据赋给它的不同子类对象,动态调用属于子类的该方法。这样的方法调用在编译期是无法确定的。
Person e = new Student();
e.getInfo(); // 调用Student 类的getInfo() 方法
编译时类型和运行时类型
时 编译时e 为Person 类型,而方法的调用是在运行时确定的,所以调用的是Student类的getInfo() 方法。—— 动态绑定
虚拟 方法调用举例
前提:
Person类中定义了welcome()方法,各个
子类重写了welcome()。
执行:
多态的情况下,调用对象的welcome()方法,
实际执行的是子类重写的方法。
方法的重载与重写
- 二者的定义细节:略
- 从编译和运行的角度看:
重载,是指允许存在多个同名方法,而这些方法的参数不同。编译器根据方法不
同的参数表,对同名方法的名称做修饰。对于编译器而言,这些同名方法就成了
不同的方法。它们的调用地址在编译期就绑定了。Java的重载是可以包括父类
和子类的,即子类可以重载父类的同名不同参数的方法。
所以:对于重载而言,在方法调用之前,编译器就已经确定了所要调用的方法,
这称为“早绑定”或“静态绑定”;
而对于多态,只有等到方法调用的那一刻,解释运行器才会确定所要调用的具体
方法,这称为“晚绑定”或“动态绑定”。
引用一句Bruce Eckel的话:“不要犯傻,如果它不是晚绑定,它就不是多态。”
多态作用:
提高了代码的通用性,常称作接口重用
前提 :
需要存在继承或者实现关系
有方法的重写
成员方法:
编译时:要查看引用变量所声明的类中是否有所调用的方法。
运行时:调用实际new的对象所属的类中的重写方法。
成员 变量:
不具备多态性,只看引用变量所声明的类。
instanceof 关键字的使用
x instanceof A :检验x 是否为类A 的对象,返回值为boolean 型。
要求x所属的类与类A必须是子类和父类的关系,否则编译错误。
如果x属于类A的子类B,x instanceof A值也为true。
对象类型转换
对象类型转换 (Casting )
基本数据类型的Casting: :
自动类型转换:小的数据类型可以自动转换成大的数据类型
如long g=20; double d=12.0f
强制类型转换:可以把大的数据类型强制转换(casting)成小的数据类型
如 float f=(float)12.0; int a=(int)1200L
对 对Java 对象的强制类型转换称为造型
从子类到父类的类型转换可以自动进行
从父类到子类的类型转换必须通过造型( 强制类型转换) 实现
无继承关系的引用类型间的转换是非法的
在造型前可以使用instanceof 操作符测试一个对象的类型
向下转型的几个常见问题
1、父类引用指向子类对象,而子类引用不能指向父类对象。
2、把子类对象直接赋给父类引用叫upcasting向上转型,向上转型不用强制转换吗,如:
Father f1 = new Son();
3、把指向子类对象的父类引用赋给子类引用叫向下转型(downcasting),要强制转换,如:
f1 就是一个指向子类对象的父类引用。把f1赋给子类引用 s1 即 Son s1 = (Son)f1;
其中 f1 前面的(Son)必须加上,进行强制转换。
子类继承父类
若子类重写了父类方法,就意味着子类里定义的方法彻底覆盖了父类里的
同名方法,系统将不可能把父类里的方法转移到子类中。
对于实例变量则不存在这样的现象,即使子类里定义了与父类完全相同的
实例变量,这个实例变量依然不可能覆盖父类中定义的实例变量
Object 类结构的剖析
Object类是所有Java类的根父类
如果在类的声明中未使用extends关键字指明其父类,则默认父类
为java.lang.Object类
== 操作符与equals 方法
==:
基本类型比较值:只要两个变量的值相等,即为true。
int a=5; if(a==6){…}
引用类型比较引用(是否指向同一个对象):只有指向同一个对象时,==才
返回true。
Person p1=new Person();
Person p2=new Person();
if (p1==p2){…}
用“==”进行比较时,符号两边的 数据类型必须兼容(可自动转换的基本
数据类型除外),否则编译出错
equals() :所有类都继承了Object, ,了 也就获得了equals() 方法 。 还可以重写 。
只能比较引用类型 , 其作用与 “==” 相同, 比较是否指向同一个对象 。
格式:obj1.equals(obj2)
特例用 :当用equals() 方法进行比较时 ,类 对类File 、String 、Date 及包装类
( (Wrapper Class) ) 来说 , 是比较类型及内容而不考虑引用的是否是同一个对
象 ;
原因:在这些类中重写了Object 类的equals() 方法 。
当自定义使用equals()时 时 , 可以重写 。 用于比较两个对象的 “ 内容 ” 是否都
相等
重写 equals()
对称性:如果x.equals(y)返回是“true”,那么y.equals(x)也应该返回是
“true”。
自反性:x.equals(x)必须返回是“true”。
传递性:如果x.equals(y)返回是“true”,而且y.equals(z)返回是“true”,
那么z.equals(x)也应该返回是“true”。
一致性:如果x.equals(y)返回是“true”,只要x和y内容一直不变,不管你
重复x.equals(y)多少次,返回都是“true”。
任何情况下,x.equals(null),永远返回是“false”;
x.equals(和x不同类型的对象)永远返回是“false”。
总结==与 equals()
面试题:== 和equals的区别
1.== 既可以比较基本类型也可以比较引用类型。对于基本类型就是比较值,对于引用类型
就是比较内存地址
2.equals 的话,它是属于java.lang.Object 类里面的方法,如果该方法没有被重写过默认也是 ==; 我们可以到 看到String类 等类的 的equals 方法是被重写过的,而且String 类在日常开发中
了 用的比较多,久而久之,形成了equals 是比较值的错误观点。
3.具体 要看自定义类里有没有 重写Object 的equals 方法来 判断。
4.写 通常情况下,重写equals.会比较类中的相应属性是否都相等
toString()的使用
toString() 方法在Object 类中定义 ,是 其返回值是String 类型 , 返回类名和它
的引用地址 。
在行 进行String 与其它类型数据的连接操作时 ,用 自动调用toString() 方法
Date now=new Date();
System.out.println(“now=”+now); 相当于
System.out.println(“now=”+now.toString());
可以根据需要在用户自定义类型中重写toString() 方法
如 如String 类重写了toString() 方法 , 返回字符串的值 。
s1=“hello”;
System.out.println(s1);// 相当于System.out.println(s1.toString());
基本类型数据转换为String 类型时 ,的 调用了对应包装类的toString() 方法
int a=10; System.out.println(“a=”+a);
面试题
public class Test1 {
public static void main(String[] args) {
char[] arr = new char[] { 'a', 'b', 'c' };
System.out.println(arr);//
int[] arr1 = new int[] { 1, 2, 3 };
System.out.println(arr1);//
double[] arr2 = new double[] { 1.1, 2.2, 3.3 };
System.out.println(arr2);//
}
}
打印结果
abc
[I@15db9742
[D@6d06d69c
包装类的理解
针对八种基本数据类型定义相应的引用类型—包装类(封装类)
有了类的特点,就可以调用类中的方法,Java才是真正的面向对象
基本数据类型转换为包装类
基本 数据类型包装成包装类的实例 --- 装箱
通过包装类的构造器实现:
int i = 500; Integer t = new Integer(i);
还可以通过字符串参数构造包装类对象:
Float f = new Float(“4.56”);
Long l = new Long(“asdf”); //NumberFormatException
获得 包装类对象中包装的基本类型变量 --- 拆箱
调用包装类的.xxxValue()方法:
boolean b = bObj.booleanValue();
JDK1.5之后,支持自动装箱,自动拆箱。但类型必须匹配。
基本数据类型包装类与 String 的相互转换
字符串转换成基本数据类型
通过包装类的构造器实现:
int i = new Integer(“12”);
通过包装类的parseXxx(String s)静态方法:
Float f = Float.parseFloat(“12.1”);
基本数据类型转换成字符串
调用字符串重载的valueOf()方法:
String fstr = String.valueOf(2.34f);
更直接的方式:
String intStr = 5 + “”
包装类常见面试题 o1 和 o2 一样吗
Object o1 = true ? new Integer(1) : new Double(2.0);
System.out.println(o1);//
Object o2;
if (true)
o2 = new Integer(1);
else
o2 = new Double(2.0);
System.out.println(o2);//
static 关键字的引入
当我们编写一个类时,其实就是在描述其对象的属性和行为,而并没有产生实质上的对象,只有通过new关键字才会产生出对象,这时系统才会分配内存空间给对象,其方法才可以供外部调用。我们有时候希望无论是否产生了对象或无论产生了多少对象的情况下,某些特定的数据在内存空间里只有一份,例如所有的国人都有个国家名称,每一个中国人都共享这个国家名称,不必在每一个中国人实例对象中都单独分配一个用于代表国家名称的变量。
class Circle{
private double radius;
public Circle(double radius){this.radius=radius;}
public double findArea(){return Math.PIradiusradius;}}
创建两个Circle对象
Circle c1=new Circle(2.0); //c1.radius=2.0
Circle c2=new Circle(3.0); //c2.radius=3.0
Circle类中的变量radius是一个实例变量(instance variable),它属于类的
每一个对象,不能被同一个类的不同对象所共享。
上例中c1的radius独立于c2的radius,存储在不同的空间。c1中的radius
变化不会影响c2的radius,反之亦然。
如果想让一个类的所有实例共享数据,就用类变量!
类属性、类方法的设计思想
类属性作为该类各个对象之间共享的变量。 在设计类时, 分析 哪
些属性 不因对象的不同而改变 ,将这些属性设置为类属性。相应
的方法设置为类方法。
如果方法与调用者无关,则这样的方法通常被声明为类方法,由
于 不需要创建对象就可以调用类方法 ,从而简化了方法 的 调用。
静态变量与实例变量的对比
使用范围:
在Java类中,可用static修饰属性、方法、代码块、内部类
被修饰后的成员具备以下特点:
随着类的加载而加载
优先于对象存在
修饰的成员,被所有对象所共享
访问权限允许时,可不创建对象,直接被类调用
实例对象需要创建对象才可以引用,而静态变量可以通过类名调用,也可使使用对象调用
类变量和实例变量的内存解析
static 修饰方法
没有对象的实例时,可以用 类名. 方法名()的形式访问由static修饰的类方法。
在static 方法内部只能访问类的 的static 修饰的属性或方法, 不能访问类的 非
static 的结构
因为不需要实例就可以访问static 方法,因此static 方法内部不能有this 。(也 也
有 不能有super ? YES!)
static 修饰的方法不能被重写
设计模式与单例设计模式
详细设计请找目录中的单例设计模式@小猪童鞋简书 目录检索
理解 main()方法的语法
由于Java虚拟机需要调用类的main()方法,所以该方法的访问权限必须是
public,又因为Java虚拟机在执行main()方法时不必创建对象,所以该方法必须
是static的,该方法接收一个String类型的数组参数,该数组中保存执行Java命令
时传递给所运行的类的参数。
又因为main() 方法是静态的,我们不能直接访问该类中的非静态成员,必须创
建该类的一个实例对象后,才能通过这个对象去访问类中的非静态成员,这种情
况,我们在之前的例子中多次碰到。
此处,Something类的文件名叫OtherThing.java
class Something {
public static void main(String[] something_to_do) {
System.out.println("Do something ...");
}
}
上述程序是否可以正常编译、运行?
可以的,但是如果是使用 public 修饰的类,文件名就必须和main的主类名一致
类中代码块结构的使用
代码块(或初始化块)的作用:
对 对Java 类或对象进行 初始化
代码块(或初始化块)的分类:
一个类中代码块若有修饰符,则只能被static修饰,称为 静态代码块
(static block),没有使用static修饰的,为 非静态代码块 。
static 代码块化 通常用于初始化static 的属性
class Person {
public static int total;
static {
total = 100;// 为total 赋初值
}
…… //其它属性或方法声明
}
静态代码块:用static 修饰的代码块
- 可以有输出语句。
- 可以对类的属性、类的声明进行初始化操作。
- 不可以对非静态的属性初始化。即:不可以调用非静态的属性和方法。
- 若有多个静态的代码块,那么按照从上到下的顺序依次执行。
- 静态代码块的执行要先于非静态代码块。
- 静态代码块随着类的加载而加载,且只执行一次。
非静态代码块:没有static 修饰的代码块 - 可以有输出语句。
- 可以对类的属性、类的声明进行初始化操作。
- 除了调用非静态的结构外,还可以调用静态的变量或方法。
- 若有多个非静态的代码块,那么按照从上到下的顺序依次执行。
- 每次创建对象的时候,都会执行一次。且先于构造器执行。
属性赋值的先后顺序(完结篇)
final 修饰类和方法
在Java中声明类、变量和方法时,可使用关键字final来修饰,表示“最终的”。
final 标记的类不能被继承 。提高安全性,提高程序的可读性。
String类、System类、StringBuffer类
final 标记的方法不能被子类重写。
比如:Object类中的getClass()。
final 标记的变量( 成员变量或局部变量) 即称为常量 。名称大写,且只
能被赋值一次。
final标记的成员变量必须在声明时或在每个构造器中或代码块中显式赋
值,然后才能使用。
final double MY_PI = 3.14;
面试题
public int addOne(final int x) {
return ++x;
// return x + 1; //修改
}
抽象类与抽象方法的使用
随着继承层次中一个个新子类的定义,类变得越来越具体,而父类则更一
般,更通用。类的设计应该保证父类和子类能够共享特征。有时将一个父
类设计得非常抽象,以至于它没有具体的实例,这样的类叫做抽象类。
用abstract关键字来修饰一个类,这个类叫做抽象类。
用abstract来修饰一个方法,该方法叫做抽象方法。
抽象方法:只有方法的声明,没有方法的实现。以分号结束:
比如:public abstract void talk();
含有抽象方法的类必须被声明为抽象类。
抽象类不能被实例化。抽象类是用来被继承的,抽象类的子类必须重
写父类的抽象方法,并提供方法体。若没有重写全部的抽象方法,仍
为抽象类。
不能用abstract修饰变量、代码块、构造器;
不能用abstract修饰私有方法、静态方法、final的方法、final的类。
抽象的应用场景举例
解决方案
Java 允许类设计者指定:超类声明一个方法但不提供实现,该方法的实现由子类提供。
这样的方法称为 抽象方法 。有一个或更多抽象方法的类称为 抽象类 。
Vehicle是一个抽象类,有两个抽象方法
创建抽象类的匿名子类对象
public abstract class Test3 {
public abstract void sayHello1();
public abstract void sayHello2();
public void sayHello3() {
System.out.println("Test3.sayHello3()");
}
}
public class Test1 {
public static void main(String[] args) {
Test3 test3 = new Test3() {
@Override
public void sayHello2() {
System.out.println("Test1.main(...).new Test3() {...}.sayHello2()");
}
@Override
public void sayHello1() {
System.out.println("Test1.main(...).new Test3() {...}.sayHello1()");
}
};
test3.sayHello1();
test3.sayHello2();
test3.sayHello3();
}
}
测试
Test1.main(...).new Test3() {...}.sayHello1()
Test1.main(...).new Test3() {...}.sayHello2()
Test3.sayHello3()
接口的理解
一方面,有时必须从几个类中派生出一个子类,继承它们所有的属性和方
法。但是,Java不支持多重继承。有了接口,就可以得到多重继承的效果。
另一方面,有时必须从几个类中抽取出一些共同的行为特征,而它们之间又
没有is-a的关系,仅仅是具有相同的行为特征而已。例如:鼠标、键盘、打
印机、扫描仪、摄像头、充电器、MP3机、手机、数码相机、移动硬盘等都
支持USB连接。
接口就是规范,定义的是一组规则,体现了现实世界中“如果你是/要...则
必须能...”的思想。继承是一个"是不是"的关系,而接口实现则是 "能不能"
的关系。
接口的本质是契约,标准,规范,就像我们的法律一样。制定好后大家都
要遵守。
接口(interface)是抽象方法和常量值定义的集合。
接口的特点:
用interface来定义。
接口中的所有成员变量都默认是由public static final修饰的。
接口中的所有抽象方法都默认是由public abstract修饰的。
接口中没有构造器。
接口采用多继承机制。
接口的定义与使用
public interface Runner {
public static final int ID = 1;
public abstract void start();
public abstract void run();
public abstract void stop();
// 等价于
/**
* int ID = 1;
* void start();
* public void run();
* void stop();
*/
}
定义Java类的语法格式:先写extends,后写implements
class SubClass extends SuperClass implements InterfaceA{ }
一个类可以实现多个接口,接口也可以继承其它接口。
实现接口的类中必须提供接口中所有方法的具体实现内容,方可实
例化。否则,仍为抽象类。
接口的主要用途就是被实现类实现。(面向接口编程)
与继承关系类似,接口与实现类之间存在多态性
接口和类是并列关系,或者可以理解为一种特殊的类。从本质上讲,
接口是一种特殊的抽象类,这种抽象类中只包含常量和方法的定义
(JDK7.0及之前),而没有变量和方法的实现。
接口的多实现与接口的继承性
一个类可以实现多个无关的接口
与继承关系类似,接口与实现类之间存在多态性
接口和抽象类之间的对比
创建接口匿名实现类的对象
public class Test1 {
public static void main(String[] args) {
Runner runner = new Runner() {
@Override
public void stop() {
System.out.println("Test1.main(...).new Runner() {...}.stop()");
}
@Override
public void start() {
System.out.println("Test1.main(...).new Runner() {...}.start()");
}
@Override
public void run() {
System.out.println("Test1.main(...).new Runner() {...}.run()");
}
};
runner.start();
runner.run();
runner.stop();
}
}
在开发中,常看到一个类不是去继承一个已经实现好的类,而是要么继承抽象类,要么实现接口。
面试题
interface A {
int x = 0;
}
class B {
int x = 1;
}
class C extends B implements A {
public void pX() {
System.out.println(x); //此处有错 super.x 就没错了
}
public static void main(String[] args) {
new C().pX();
}
}
或者修改为
interface A {
int y = 0;
}
class B {
int x = 1;
}
class C extends B implements A {
public void pX() {
System.out.println(y);
}
public static void main(String[] args) {
new C().pX();
}
}
Java8 中接口的新特性
Java 8中,你可以为接口添加静态方法和默认方法。从技术角度来说,这是完
全合法的,只是它看起来违反了接口作为一个抽象定义的理念。
静态方法:使用 static 关键字修饰。可以通过接口直接调用静态方法,并执行
其方法体。我们经常在相互一起使用的类中使用静态方法。你可以在标准库中
找到像Collection/Collections或者Path/Paths这样成对的接口和类。
默认方法:默认方法使用 default 关键字修饰。可以通过实现类对象来调用。
我们在已有的接口中提供新方法的同时,还保持了与旧版本代码的兼容性。
比如:java 8 API中对Collection、List、Comparator等接口提供了丰富的默认
方法。
接口中的默认方法
若一个接口中定义了一个默认方法,而另外一个接口中也定义了一个同名同
参数的方法(不管此方法是否是默认方法),在实现类同时实现了这两个接
口时,会出现: 接口冲突。
解决办法:实现类必须覆盖接口中同名同参数的方法,来解决冲突。
若一个接口中定义了一个默认方法,而父类中也定义了一个同名同参数的非
抽象方法,则不会出现冲突问题。因为此时遵守: 类优先原则。接口中具有
相同名称和参数的默认方法会被忽略。
冲突
interface A {
int y = 0;
static void help() {
System.out.println("A.help()");
}
}
interface D {
int y = 0;
static void help() {
System.out.println("D.help()");
}
}
class B {
int x = 1;
}
class C extends B implements A,D{
public void pX() {
System.out.println(A.y);
}
public static void main(String[] args) {
new C().pX();
A.help();
}
}
内部类的分类
当一个事物的内部,还有一个部分需要一个完整的结构进行描述,而这个内
部的完整的结构又只为外部事物提供服务,那么整个内部的完整结构最好使
用内部类。
在Java中,允许一个类的定义位于另一个类的内部,前者称为内部类,后者
称为外部类。
Inner class一般用在定义它的类或语句块之内,在外部引用它时必须给出完
整的名称。
Inner class的名字不能与包含它的外部类类名相同;
: 分类: 成员 内部类(static成员内部类和非static成员内部类)
局部内部类(不谈修饰符)、匿名内部类
成员内部类的特点
成员内部类作为 类 的 成员的角色:
和外部类不同,Inner class还可以声明为private或protected;
可以调用外部类的结构
Inner class 可以声明为static的,但此时就不能再使用外层类的非static的成员
变量;
成员内部类 作为类的角色:
可以在内部定义属性、方法、构造器等结构
可以声明为abstract类 ,因此可以被其它的内部类继承
可以声明为final的
编译以后生成OuterClass$InnerClass.class字节码文件(也适用于局部内部类)
【注意】
- 非static的成员内部类中的成员不能声明为static的,只有在外部类或static的成员
内部类中才可声明static成员。 - 外部类访问成员内部类的成员,需要“内部类.成员”或“内部类对象.成员”的方式
- 成员内部类可以直接使用外部类的所有成员,包括私有的数据
- 当想要在外部类的静态成员部分使用内部类时,可以考虑内部类声明为静态的
class Outer {
private int s;
public class Inner {
public void mb() {
s = 100;
System.out.println("在内部类Inner中s=" + s);
}
}
public void ma() {
Inner i = new Inner();
i.mb();
}
}
public class InnerTest {
public static void main(String args[]) {
Outer o = new Outer();
o.ma();
}
}
如何实例化成员内部类
实例化
class Outer {
private int s;
public class Inner {
public void mb() {
s = 100;
System.out.println("在内部类Inner中s=" + s);
}
}
public void ma() {
Inner i = new Inner();
i.mb();
}
}
public class InnerTest {
public static void main(String args[]) {
Outer o = new Outer();
o.ma();
}
}
如何 使用局部内部类
只能在声明它的方法或代码块中使用,而且是先声明后使用。除此之外的任何地方都不能使用该类
但是它的对象可以通过外部方法的返回值返回使用,返回值类型只能是局部内部类的父类或父接口类型
局部内部类的特点
内部类仍然是一个独立的类,在编译之后内部类会被编译成独立的.class文件,但
是前面冠以外部类的类名和$符号,以及数字编号。
只能在声明它的方法或代码块中使用,而且是先声明后使用。除此之外的任何地方
都不能使用该类。
局部内部类可以使用外部类的成员,包括私有的。
局部内部类可以使用外部方法的局部变量,但是必须是final的。由局部内部类和局
部变量的声明周期不同所致。
局部内部类和局部变量地位类似,不能使用public,protected,缺省,private
局部内部类不能使用static修饰,因此也不能包含静态成员
匿名内部类
匿名内部类不能定义任何静态成员、方法和类,只能创建匿名内部类的一
个实例。一个匿名内部类一定是在new的后面,用其隐含实现一个接口或
实现一个类。
格式:
new 父类构造器(实参列表)| 实现接口(){
// 匿名内部类的类体部分
}
匿名内部类的特点
匿名内部类必须继承父类或实现接口
匿名内部类只能有一个对象
匿名内部类对象只能使用多态形式引用