1.继承(关键字extends)
继承是两个类之间的关系。当类Son继承了一个已存在的类Father后,类Son就拥有了类Father所有的非private属性和方法,同时还可以在Son类中添加新的属性和方法。原来已存在的类称为父类或基类,也可以称为超类。新派生的类称为原来的子类或是派生类。
子类继承父类语法:
访问控制符 [修饰符] class 子类名 extends 父类名{
[属性定义]
[构造方法定义]
[方法声明]
}
语法说明:
- 访问修饰符:访问修饰符是public,那么任意类均可以使用这个类,如果不写,则只有与该类定义在同一包内才可以访问这个类
- 修饰符:可选,有static final transient volatile
- 在声明类时,使用extends关键字表明两个类之间的继承关系
- 子类可以定义自身的属性和方法,如果子类定义的属性或方法与父类相同,那么父类的这一属性或方法被隐藏或重写。通过子类或子类的对象不能直接调用父类的这一属性或方法,但是父类或父类的对象依然可以调用这一属性或方法。当子类执行从父类继承来的方法,若需使用同名的成员变量时,所使用的是父类的成员变量。
- 在类声明时,若没有使用extends关键字继承父类,则自动继承Object类,因此除了根类Object之外,所有的Java类都直接或间接的继承java.lang.Object类
Java继承的特点:
- 继承关系可以传递,即子类向上兼容继承父类的所有属性和方法,非私有的是显式继承,私有的属性和方法是隐式继承,私有的属性和方法可通过getter、setter的方式访问。
- 继承简化了人们对事物的认识和描述,能清晰的体现相关类间的层次结构关系
- 继承减小了代码和数据的冗余度,增加了程序的重用性。
- Java只支持单继承(C++是多继承的)即
class A extends B,C
的语法是错误的编译不通过,但可以多层继承即class B extends C{....} class A extends B
。
若子类和父类在同一个包内,子类不能直接访问父类和private的属性和方法,但可以调用非private的属性和方法。若子类和父类不在同一个包内,那么子类可以访问父类protected、public 的属性和方法,但不能访问私有、默认访问级别的属性和方法。
重写父类的方法
在继承中,不仅可以使用方法重载(在同一个类中实现两个或两个以上方法名相同,参数列表不同的方法)还可以实现方法的重写。
子类中定义的方法使用的方法名、返回类型和参数列表与父类中的方法一样,则称为子类的方法重写了父类的方法。
父类的构造方法不能被重写,因为构造方法与所在类的类名相同,而子类和父类的类名不同。
当父类的方法被重写后,子类或子类对象调用的是被重写之后的方法,父类或父类的对象还是可以调用被重写之前的方法。
使用方法重写的约束:
- 子类的方法名,返回值类型,参数列表必须与父类的方法名,返回值类型,参数列表完全一致。
- 子类不能缩小父类方法的访问权限。
- 父类的静态方法不能被子类重写为非静态方法,父类的非静态方法也不能被子类重写为静态方法,子类可以定义与父类的静态方法同名的静态方法,以便在子类中隐藏父类的静态方法。
- 子类方法不能抛出比父类方法更多的异常
- 父类中的私有方法不能被子类重写
- 父类中的抽象方法可以被子类通过两种途径重写:1.实现父类的抽象方法2.子类重新声明父类的抽象方法
- 父类中的非抽象方法可以被子类重写为抽象方法
构造方法与继承
在继承中,子类对象在实例化之前必须先调用父类的构造方法,再调用子类自身的构造方法,在子类中,通过关键字super调用 与super([参数列表])
方法中参数列表相同的父类的构造方法,若子类中没有特别指定super([参数列表])方法,则默认添加并调用无参的super()方法。
调用父类构造方法的super()方法要写在子类构造方法的首行。
this()和super()调用构造方法时不能同时出现,因为都需要放在首行。
super关键字
在继承中,使用super关键字可以引用父类的属性、方法和构造方法。
如果在子类中隐藏了父类的属性及重写了父类的方法,那么需要在子类中调用父类被覆盖的属性或方法就不能直接调用了,需要使用super关键字。
内部类
在一个类Outer的内部定义一个类Inner,此时Outter称为外部类,Inner称为内部类。类的定义是可以多层嵌套的。
内部类中定义的成员要比外部定义的成员具有更严格的隐藏信息功能。
内部类的作用:
- 内部类的对象可以访问外部类的所有属性和方法,包括私有的,因为内部类被当作了外部类的成员,同一个类的成员可以相互访问。但是外部类不能访问内部类中的实现细节,这一特点弥补了继承的缺憾。
class Outer{
private String msg = "这是写在外部类的私有属性";
//定义一个成员内部类
class Inner{
int num=2;
public void print(){
//打印外部类的私有属性
System.out.println(msg);
}
}
public void fun(){
Inner in =new Inner();
in.print();
}
}
public class InnerTest{
public static void main(String[] args){
//创建外部类对象,调用内部类方法
Outer ou =new Outer();
ou.fun();
}
}
2. 内部类提供了更好的封装,可以隐藏在外部类之中,不被同一个包的其他类所见。
3. 匿名内部类可以方便 的定义运行时回调和用于仅需要一次使用的类
顶级类(最外层的类)只有两种访问控制权限public与默认;内部类有四种访问控制权限:public protected 默认 private
在外部类中创建内部类内部类.内部对象名=new 内部类();
在外部类以外的其他类中访问内部类外部类.内部类 内部类对象 = new 外部类().new 内部类();
class Outer{
//定义外部类的私有属性
private String s="this is 外部类";
//声明内部类
public class Inner{
//定义内部类的方法
public void add(int x,int y){
System.out.println(x+"+"+y+"="+(x+y));
}
public void getStr(){
//内部类中直接访问外部类成员属性
System.out.println(s);
}
}
//从外部调用内部类的方法
public void getInofo(){
//匿名内部类对象调用外部类方法
new Inner().getStr();
//声明创建内部类实例对象
Inner in = new Inner();
//实例调用内部类方法
in.add(7,8);
}
}
public class InnerTest2{
public static void main(String[] args){
//在其他类中创建外部类的实例对象
Outer out=new Outer();
//外部类实例调用外部类的方法
out.getInofo();
//在其他类中创建内部类的实例对象
Outer.Inner in = new Outer().new Inner();
//调用内部类的方法
in.add(1,2);
in.getStr();
}
}
内部类的分类
成员内部类是依附于外围类的,只有先创建了外围类才能创建内部类
(1)实例内部类
实例内部类是指声明在外部的方法体外的,即与外部类的成员(属性、方法)同级。没有static修饰的内部类。
实例内部类的特点
- 在实例内部类中不能存在任何静态的变量或方法。
- 在外部类的静态方法或外部类以外的其他类,若需要访问内部类,则必须通过外部类创建内部类的访问实例访问。
外部类.内部类 内部类对象 = new 外部类().new 内部类();
- 实例内部类可以访问外部类的所有成员属性,在外部类中不能直接访问内部类的成员,必须通过内部类的实例访问。
- 多层嵌套中,Outer类包含Inner1,Inner1类又包含Inner2,则在Outer类中不能直接访问Inner2,应通过Inner1的实例访问Inner2类。
class Outer{
//声明第一层内部类
public class Inner1{
//声明第二层内部类
public class Inner2{
//定义内部类的方法
public void add(int x,int y){
System.out.println(x+"+"+y+"="+(x+y));
}
}
}
//外部类中定义普通方法想要访问Inner2类
public void getAdd(){
//外部类通过 Inner1的实例访问Inner2类
Inner1.Inner2 in= new Inner1().new Inner2();
in.add(3,4);
}
}
- 如果在实例内部类Inner与外部类Outer有着同名的成员变量i,则在内部类中,i、this.i、Inner.this.i 都表示Inner类的成员,而Outer.this.i才表示外部类Outer的成员。
class Outer{
//外部类的成员属性
int i=10;
public class Inner{
//内部类的同名成员属性
int i=20;
//内部类的方法
public void getOuter(){
System.out.println("i表示Inner的成员i:"+i);//20
System.out.println(this.i);//20
System.out.println(Inner.this.i);//20
System.out.println(Outer.this.i);//10
}
}
//外部类的同名方法
public void getOuter(){
System.out.println(Outer.this.i);//10
}
}
public class InnerTest3{
public static void main(String[] args){
//通过匿名内部类的对象调用内部类的方法
new Outer().new Inner().getOuter();
//通过匿名外部类的对象调用外部类的方法
new Outer().getOuter();
}
}
(2)静态内部类
静态内部类是声明在外部类中,作为外部类的静态成员的,即使用static 关键字修饰的内部类。与实例内部类只能声明非静态成员不同,静态内部类既可以声明静态成员,也可以声明非静态成员,静态内部类不能访问外部类的非静态成员,只能访问外部类的静态成员(只能通过外部类的实例或对象访问)。静态内部类的对象实例可以独立创建,静态内部类类似于顶层类,只不过被定义在了一个类的内部。
静态内部类创建对象实例:
外部类.静态内部类 静态内部类对象名 = new 外部类.静态内部类();
class Outer{
//定义外部类属性
private int a=4;
//定义外部类静态属性
private static int b=5;
//定义静态内部类
public static class Inner{
private int x=5;//可定义非静态属性
private static int y=6;//可定义静态属性
//可定义非静态方法
public void add(int x,int y){
int temp=new Outer().a;
//int temp=a;//error
int x=b;//在静态内部类中可直接访问外部类的静态属性
System.out.println(temp+"x"+x+"="+(temp+x));
}
//可定义静态方法
public static void reduce(int x,int y){
System.out.println(x+"-"+y+"="+(x-y));
}
}
//外部类的普通方法
public void getInfo(){
//通过静态内部类的匿名对象调用内部类的方法
new Inner().add(4,3);
//可通过静态内部类的类名直接调用静态内部类的方法
Inner.reduce(4,3);
}
}
public class StaticInner{
public static void main(String[] args){
//创建外部类实例
Outer out=new Outer();
out.getInfo();
//创建内部类实例
Outer.Inner in =new Outer. Inner();
in.add(4,3);
in.reduce(4,3);
}
}
(3)局部内部类
局部内部类是指定义在方法体的内部类,局部内部类仅在该方法有效。
局部内部类不能被外部类以及外部类以外的其他类访问,因此局部内部类是不需要(也不能被)访问控制符和static修饰的,
局部内部类不能定义static成员,
局部内部类可以访问外部类的静态成员,
若需调用外部类的非静态成员,可以通过外部类的实例。
局部内部类只能访问所在方法的final类型的参数和变量,若不声明,系统自动将变量或形参转为final类型的变量(JDK8形参变为隐式final声明).
class Outer{
//定义外部类的非静态私有属性
private float f=0.1f;
//定义外部类的静态私有属性
private static int a=7;
//定义外部类的普通方法
public void fun(){
//在普通方法里定义内部类称为局部(方法内部类)
class Inner{
//方法内部类中定义私有属性
private float innerF=4.3f;
public Inner(){
System.out.println(f+"+"+innerF+"="+(f+innerF));
}
}
//在外部类普通方法里创建内部类匿名对象,JVM才能加载方法内部类
new Inner();
}
//外部类中定义静态方法
public static void fun2(){
//静态方法中定义内部类,也不能加sattic 和 访问修饰符
class Inner2{
private int i=2;
public Inner2(){
//在静态方法内部类中直接调用外部类的静态私有属性a
System.out.println(i+"+"+a+"="+(i+a));
//在静态方法内部类中直接通过匿名对象调用外部类的私有属性
float temp= new Outer().f;
System.out.println(i+"+"+temp+"="+(i+temp));
}
}
//在外部类静态方法里创建内部类匿名对象
new Inner2();
}
}
public class PartInner{
public static void main(String[] args){
Outer out = new Outer();
out.fun();
out.fun2();
}
}
(4)匿名内部类
匿名内部类是指在定义时没有名称的类,必须在声明时使用new语句 声明类。匿名内部类是一种特殊的内部类,除了具有普通类的特点以外,还有自己的特点。匿名内部类一般只使用一次。
匿名内部类语法:
new <类或接口>([参数列表]){
.....
}
参数列表表示调用父类构造方法时传入的参数,匿名内部类只在其定义的代码块内使用一次,所有无法为其定义构造方法。匿名内部类总是使用父类的构造方法创建实例,如果匿名内部类实现的时接口,那么匿名内部类的构造方法就是Object().
虽然匿名内部类没有类名,匿名内部类必须扩展一个基类或实现一个接口,但不能明显的使用extends 或 implements关键字。若匿名内部类继承抽象类或实现接口时,还要实现父类及接口中的所有抽象方法。有名称的类若没有显式的指定父类,系统会让其自动继承Object类,但匿名内部类不会自动继承Object类,所有每个匿名内部类都要明确的指出它继承的类或实现的接口。
匿名内部类继承抽象类
abstract class AbstrClass{//定义抽象类
public abstract void getInfo();//声明抽象类
}
class Outer{//定义类(匿名内部类的外部类)
public void print(){
//在print方法中调用show方法,show方法的形参中开辟了抽象类的匿名对象
//实现了抽象类的抽象方法,使之成为匿名内部类
show(new AbstrClass(){
public void getInfo(){ //实现了抽象类中的方法
System.out.println("Java匿名内部类");
}
});//show方法的调用实现了Outer类继承抽象类成为匿名内部类
}
public void show(AbstrClass a){//定义show 方法,形参为AbstrClass类的实例对象
a.getInfo();
}
}
public class AnonymityInner{
public static void main(String[] args){
new Outer().print();//创建外部部类的对象并调用匿名内部类的方法
}
}
匿名内部类实现接口
interface Inter{//定义接口类Inter
public abstract void getInfo();
}
class InterClass{//定义类InnerClass(匿名内部类的外部类)
public void print(){//定义类InnerClass类中的普通方法
show(new Inter(){//调用show方法
//在show方法的参数中通过匿名对象实现接口类的抽象方法,使此类成为匿名内部类
public void getInfo(){
System.out.println("Java匿名内部类,实现接口");
}
});
}
public void show(Inter i){
i.getInfo();
}
}
public class AnonymityInner{
public static void main(String[] args){
new InterClass().print();//创建外部类 InterClass的对象并调用方法
}
}
匿名内部类可以访问外部类的所有成员,但匿名内部类定义在方法之中,只能访问方法中的final类型的参数和局部变量。
abstract class AbstrClass{//定义抽象类
public abstract void getInfo();//声明抽象类
}
class InterClass2{
public void print(int i,final int k){
int x=10;
final int y =20;
show(new Inter(){
public void getInfo(){
System.out.println("print方法的final类型参数:"+k);
System.out.println("print方法的final类型局部变量:"+y);
System.out.println("print方法的非final类型参数:"+i);
System.out.println("print方法的非final类型局部变量:"+x);
}
});
}
public void show(Inter i){
i.getInfo();
}
}
public class AnonymityInner{
public static void main(String[] args){
new InterClass2().print(3,5);
}
}
匿名内部类定义在方法之中,能访问方法中的非final类型的参数和局部变量是因为JDK8会将局部变量和形参变为隐式final声明
匿名内部类允许非静态代码块对成员进行初始化:
class InterClass3{
public void print(){
show(new Inter(){
int x;
{
x=10;
}
public void getInfo(){
System.out.println("x="+x);
}
});
}
public void show(Inter i){
i.getInfo();
}
}
public class AnonymityInner{
public static void main(String[] args){
new InterClass3().print();//创建inner匿名内部类的对象并调用匿名内部类的方法
}
}