JavaSE面向对象(下)

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_36575247/article/details/83098598

JavaSE面向对象(下)

初始化代码块

普通初始化代码块

又叫对象初始化代码块
代码块的执行是顺序执行先定义先执行,语法如下

	{
		//可执行代码
	}

这个代码块会出现在成员变量之前会在实例化对象时使用代码块中的逻辑代码对对像进行初始化。

静态初始化代码块

静态初始化代码块又叫类初始化代码块
语法如下

static{
		//可执行代码
}

静态初始化代码块是系统在初始化类时执行而不是创建对象来执行的。因此他比普通代码块先执行。

final关键字

final 成员变量

成员变量是随着对象的初始化而初始化的。也就是说当执行静态代码块
,普通初始化代码块,构造器时可以给出实例变量初值,但是如果,用final修饰后在一开始的时候如果没有给出默认值,那么成员变量会一直是系统给出的默认值0,‘\u0000’,false,null。这些成员变量也就失去了存在的意义。因此Java与法规定:final修饰的成员变量必须由程序员显示的指定初始值。

final修饰局部变量

局部变量中系统不会对其进行初始化,局部变量必须由程序员显示的初始化。因此在使用final修饰的局部变量时,既可以在定义时指定默认初值,也可以不指定默认值,在后来的代码中对该final进行赋值,但是只能赋值一次。

final修饰基本类型变量和引用类型变量的区别

当使用final修饰基本变量类型时,基本类型的值不能被改变,但引用类型只保存了一个引用,final只能保证其保存的地址不会被改变,即一直引用到同于一个对象。但这个对象不会被改变。

可执行“宏替换”的final变量

在C/C++中大家都了解过宏定义 #define他在代码编译过程中会直接替换掉所定义的变量,Java中也会出现这个情况当
1.使用final修饰符修饰;
2.在定义时已经给出了初值;
3.该初值在编译时已经确定。
符合上面三个条件时这个变量就相当于一个直接量。
比如:

final int a = 50;
System.out.println(a);

对于这个程序来说,a就根本不存在,实际是执行了
System.out.println(50);
这就相当于C/C++中的宏定义。

final方法

用final修饰的方法不可以被重写,如果出于某些原因不希望子类重写父类的方法时可以用final修饰。(在Java中object类中的getClass()方法就是用final修饰的,Java不希望任何类重写这个方法所以用final饰。),当你试图重写时,将会编译错误。final定义的方法只是不能被重写,但是可以被重载。

public class FinalOverload{
    public final void test(){}
    public final void test(String arg){}
}

final类

final修饰的类不可以被继承

public final class FinalClass {}
//下面的代码将出现编译错误
class Sub extends FinalClass {}

抽象类

抽象类的引入

当我们编写一个类时,我们通常会定义一些方法,但是当我们再写父类时,我们只知道子类有这些方法,但是不知道这些子类的方法具体表现所以Java引入一个只有方法签名没有方法实现的抽象类
语法如下

修饰符 abstract class 类名{
}

抽象类和抽象方法的特点

1.抽象类中必须使用abstract修饰,抽象方法也必须使用abstract修饰,抽象方法也不能有方法体
2.抽象类不能实例化,无法使用new创建实例对象
3.含有抽象方法的类必须被定义为抽象类
4.抽象类可以包含成员变量、方法、初始化块、构造器五种成分,主要适用于子类调用。

abstract使用注意事项

1.当abstract修饰类时,表明这个类只能被继承;当使用abstract修饰方法时,表明这个方法必须由子类重写。而final修饰的方法不能被重写,final修饰的类不能被继承,所以final和abstract不能同时出现。
2.abstract不能用于修饰成员变量和局部变量。
3.static修饰方法时这个方法就属于类,但是如果定义为抽象方法时,则会导致调用时出现错误,因此static和abstract不能同时修饰某个方法。(但是static和abstract并不绝对互斥,可以用来修饰内部类。)
4.abstract 修饰的方法必须被子类重写才有意义所以,abstract和private不能同时修饰方法。

抽象类的作用

抽象类不能创建实例对象,只能当成父类被继承。从语义的角度来看,抽象类是从多个具体类中抽象出的父类,具有更高层次的抽象。以这个抽象类作为其子类的模板从而避免了子类的随意性。这就体现了一个模板模式。模板模式在面向对象的软件中很常用的。

接口

接口的引入

抽象类可以给我们提供一个类的模板,但是我们如果将这种抽象抽象的更彻底,则可以提炼出一个更加特殊的“抽象类”——接口。为了体现事物功能的扩展性,Java中就提供了接口来定义这些额外功能,并不给出具体实现。

接口的语法

[修饰符] interface 接口名 extends 父接口1,父接口2...{
	//常量定义、
	//抽象方法定义
	//内部类、接口、枚举定义、
	//默认方法或类方法定义
}

接口特点

1.接口支持多继承
2.接口不能被直接实例化(可以通过多态来实例化)
3.接口的子类,可以是抽象类但是意义不大。可以是具体类,但是要重写接口中的所有抽象方法。
4.接口成员变量只能是常量,并且是静态的。默认修饰符:public static final。接口没有构造方法,成员方法只能是抽象方法,默认修饰符:public abstract

接口和抽象类

相同点
1.接口和抽象类都不能被实例化,位于继承树的顶端,只能被用来其他类的实现和继承。
2.抽象类和接口都可以包含抽象方法,子类必须都实现这些方法。
不同点
1.接口相当于时系统与外界交互的窗口,接口体现的是一种规范。抽象类是系统中大多数子类的父类,它体现的是模板的概念。
2.接口中只能包含抽象方法、静态方法和默认方法,不呢为普通方法提供实现;而抽象类可以包含普通方法。
3.接口只能定义静态常量,不能定义普通成员变量;抽象类既可以定义普通成员变量,也可以是静态常量。
4.接口中不可以包含构造器;抽象类里可以包含构造器,并不是用来创建对象而是让子类调用构造器来实现抽象类的初始化。
5.抽象类可以包含初始化块;接口不可以包含初始化块
6.抽象类只能单继承;但是接口支持多继承。

类与接口之间的关系

1.类与类
继承关系,只能是单继承但是支持多层继承
2.类与接口
实现关系,可以单实现,也可以多实现。并且还可以在继承一个类的同时实现多个接口。
3.接口与接口
继承关系,可以单继承,也可以多继承

内部类

定义:

定义在其他类内部的类称为内部类,把弄含内部类的类被称作外部类。

作用

1.内部类提供了更好的封装,可以把内部类隐藏在外部类之中,不允许同一个包中其他类访问该类。
2.内部类成员可以直接访问外部类的私有数据,因为内部类被当作外部类的成员,但是外部类不可以访问内部类的具体细节。
3.内部类适合用于创建那些仅需使用一次的类。

内部类与外部类的区别

除了位置的区别外还有
1.内部类可以使用private、protect、static
2.非静态内部类不可以拥有静态成员。

非静态内部类

定义方法如下

public class OuterClass{
	修饰符 class InClass{
	}
}

非静态内部类注意事项

非静态内部类不允许出现静态方法、成员变量、静态初始化块。

静态内部类

如果用static修饰一个内部类,则这个内部类就属于外部类本身,而不属于外部类的某个对象因此也叫类的内部类。

静态内部类的特点

1.静态内部类可以包含静态成员,也可以包含非静态成员。
2.静态内部类不可以访问外部类的非静态成员。
3.静态内部类属于外部类的一个静态成员因此外部类的所有方法所有初始化块中可以使用静态内部类来定义变量创建对象。
4.外部类不能直接访问静态内部类的成员,但是可以通过内部类的类名做调用者来访问静态内部类的类成员,也可以使用内部类对象作为调用者来访问静态内部类的实力成员。
5.接口中定义内部类时默认使用public static 修饰,也就是接口中只能定义静态内部类。、

局部内部类

如果一个类定义在方法中,则这个类就是局部内部类,局部内部类仅在该方法中有效。由于局部内部类不能再方法以外的地方使用所以不能用static和访问控制符修饰。
局部内部类是一个很“鸡肋”的语法,他只能在当前方法中使用。但是大部分时候,定义一个类之后,当然是希望多次复用这个类,因此在实际开发中很少用到

使用内部类

1.在外部类中使用内部类
外部类中使用内部类没有太多的差别,但是不要再内部类的静态成员中使用非静态内部类。同时外部类内也可以继承内部类的子类

public class SubClass extends Out.In{
	public SubClass(Out out){
		//通过传入的out对象显示调用In构造器
		out.super()
	}
 }

2.外部类外使用非静态内部类
1)省略访问控制符的内部类,只能被与外部类处于同一个包下的其他类访问
2)使用protect修饰的内部类,可以被外部类处于同一个包中的其他类和外部类的子类访问。
3)使用public修饰符的内部类,可以在任何地方访问。
4)使用private修饰的内部类,外部类以外无法访问
使用语法如下

	Out.In = new Out().new In();

5)非静态内部类的子类不一定是一个内部类,它可以是一个外部类。但是非静态内部类的子类实例一样要保留一个指向其父类所在的外部类的对象的一个引用
3.在外部类以外使用静态内部类
因为静态内部类是外部类类相关的,因此创建静态内部类对象时无需创建外部类对象。在外部类以外的地方创建静态内部类实例的语法如下:

StaticOut.StaticiIn = new OuterClass.InnerConstructor();

不管是静态内部类还是非静态内部类,他们声明变量的语法完全一样。区别只在于静态内部类只需要使用外部类调用构造器,而非静态内部类必须使用外部类对象来调用构造器。
创建静态内部类的子类也无需使用外部类的对象:

public class StaticSubClass extends StaticOut.StaticIn{ }

匿名内部类

定义:

匿名内部类适合那种只需要使用一次的类,创建匿名内部类的时候会立即创建一个这个类的实例,这个类的定义立即消失,匿名内部类不能重复使用。语法如下:

new 实现接口 | 父类构造器(实参列表){
	//匿名内部类的类体部分
}

从上面的定义可以看出,匿名内部类必须继承一个父类或实现一个接口,但是最多只能继承一个父类,或者实现一个接口。

匿名内部类的使用注意事项:

1.匿名内部类不能是抽象类,因为系统在创建匿名内部类时,会立即创建匿名内部对象,因此不允许匿名内部类是抽象类。
2.匿名内部类不能有构造器。由于匿名内部类没有类名,所以无法定义构造器,但是可以通过初始化代码块来完成构造器完成的事情。

匿名内部类实现接口

interface Product{
	public double getPrice();
	public String getName();
}
public class AnonymousTest{
	public void test(Product p){
		System.out.println("Name:"+p.getName);
		System.out.println("Price:"+p.getPrice);
	}
	public static void main(String [] args){
		AnonymouseTest ta = new AnonymouseTest;
		//传入匿名实现类的实例
		ta.test(new Prodect(){
			public double getPrice(){
				return 555.23;
			}
			public String getName(){
				return "键盘";
			}
		});
	}
}

由于test方法需要传入一个Product对象作为参数,但是Product只是一个接口不能直接创建对象。我们就可以向上面一样定义一个匿名内部类来实现。

匿名内部类实现抽象类

同样的如果传参需要一个抽象类的对象类型我们也可以使用匿名内部类的方法

package com.conpany.demo;
abstract  class Device{
    private String name;
    public abstract double getPrice();

    public Device() {
    }

    public Device(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}
public class AnonymouseInner {
    public void test(Device d){
        System.out.println("Name:" + d.getName() + "Price:" + d.getPrice());
    }

    public static void main(String[] args) {
        AnonymouseInner ai = new AnonymouseInner();
        ai.test(new Device("键盘") {
            @Override
            public double getPrice() {
                
                return 78.9;
            }
        });
        Device d = new Device() {
            {
                System.out.println("我是初始化块");
            }
            @Override
            public double getPrice() {
                return 82.2;
            }

            @Override
            public String getName() {
                return "鼠标";
            }
        };
        ai.test(d);
    }
}

猜你喜欢

转载自blog.csdn.net/qq_36575247/article/details/83098598