目录
1.final修饰类(String类以及8大基本数据类型的包装类,Integer)
代码块的定义与使用:
定义:使用“{ }”定义的一段代码。
根据代码块定义的位置以及关键字,可以分为以下四种:
1.普通代码块
2.构造块
3.静态代码块
4.同步代码块
1.普通代码块
定义在方法中的代码块。
2.构造块
定义在类中的代码块(不加修饰符)。
举个栗子:
class A{
public A(){
System.out.println("这是构造方法");
}
{
System.out.println("这是构造块");
}
}
public class Test{
public static void main(String[] args){
A a1 = new A();
A a2 = new A();
}
}
运行结果:
通过上述代码我们发现:在对象产生时,构造块优先于构造方法执行,有几个对象产生,就调用几次构造块。构造块可以完成一些属性的初始化操作(在调用构造方法前)。
3.静态代码块
使用static定义的代码块。
根据静态块所在的类不同,可以分为以下两种类型:
1.在非主类中
2.在主类中
3.1在非主类中的静态代码块
举个栗子:
class A{
public A(){
System.out.println("这是构造方法");
}
{
System.out.println("这是构造块");
}
static{
System.out.println("这是静态块");
}
}
public class Test{
public static void main(String[] args){
System.out.println("-----start-----");
A a1 = new A();
A a2 = new A();
System.out.println("-----end-----");
}
}
运行结果:
通过上述代码我们发现:静态代码块在类加载时执行,优先于构造块执行,无论有多少对象产生,只会调用一次。
静态块的主要作用是为static属性进行初始化。
3.2在主类中的静态代码块
举个栗子:
public class Test{
{
System.out.println("主类的构造块");
}
static{
System.out.println("主类的静态块");
}
public Test(){
System.out.println("主类的构造方法");
}
public static void main(String[] args){
System.out.println("-----start-----");
A a1 = new A();
A a2 = new A();
System.out.println("-----end-----");
}
}
运行结果:
通过上述代码我们可以发现:在主类中定义的代码块,优先于主方法执行。
4.同步代码块
与线程同步问题有关,在此不多加阐述。
代码块的基本知识都已经明白了,接下来我们看一道习题,结合了上述所有知识点。可以自己想想答案是什么。
class HelloA{
public HelloA(){
System.out.println("这是父类的构造方法");
}
{
System.out.println("这是父类的非静态代码块");
}
static{
System.out.println("这是父类的静态代码块");
}
}
class HelloB extends HelloA{
public HelloB(){
System.out.println("这是子类的构造方法");
}
{
System.out.println("这是子类的非静态代码块");
}
static{
System.out.println("这是子类的静态代码块");
}
}
public class Test{
public static void main(String[] args){
System.out.println("-----start-----");
new HelloB();
new HelloB();
System.out.println("-----end-----");
}
}
继承的定义与使用:
1.继承的实现
在Java中,继承使用extends关键字来实现。定义的语法如下:
class 子类 extends 父类
注:子类又时也被称为派生类,父类也被称为超类或基类。
在发生了类的继承关系后,子类可以直接继承父类的操作,实现代码的重用。
子类最低也维持和父类相同的功能。
子类可以进行功能上的扩充。
2.继承的限制
子类对象实例化前,首先调用父类构造方法产生父类对象后,再调用子类构造方法实例化子类对象。
举个栗子:
class Person{
public Person(){
System.out.println("父类对象产生");
}
}
class Student extends Person{
public Student(){
super();//此语句在无参时写与不写都一样
System.out.println("子类对象产生");
}
}
public class Test{
public static void main(String[] args){
new Student();
}
}
运行结果:
注:实际上在子类的构造方法中,相当于隐含了一条语句,super( );如果父类中没有提供无参构造,就必须使用super( )明确指明你要调用的构造方法。
Java只允许单继承,不允许多继承。(Java的单继承局限)
要想在Java中实现类似的“多继承”,要么多层继承,要么使用内部类。
//多层继承
class A(){}
class B extends A(){}
class C extends B(){}
多层继承层数不建议太多,最好不要超过3层。
在继承时,子类会继承父类的所有结构。(包含私有域与其他属性,方法)
显式继承:所有非私有操作(非private操作)属于显式继承(可以直接调用)
隐式继承:所有私有操作(private操作)属于隐式继承(不可以直接调用,需要通过其他形式调用,例如getter,setter)
覆写:
定义:子类定义了与父类方法名称,参数列表,返回值完全相同的方法。被覆写的方法不能拥有比父类更为严格的访问控制权限。
private < default < protected < public
举个栗子:
class Person{
public void print(){
System.out.println("父类的print方法");
}
}
class Student extends Person{
public void print(){
System.out.println("子类的print方法");
}
}
public class Test{
public static void main(String[] args){
new Student().print();
}
}
运行结果:
以后在进行覆写操作的时候,要注意以下两点:
a.看new在哪(当前使用的对象是通过哪个类new的)
b.调用的方法有没有被子类覆写,如果被覆写,调用的一定是被覆写后的方法。
覆写和重载的区别:
区别 | 重载(overload) | 覆写(override) |
概念 | 方法名称相同,参数的类型及个数不同 | 方法名称,返回值类型,参数的类型及个数完全相同 |
范围 | 一个类 | 继承关系 |
限制 | 没有权限要求 | 被覆写的方法不能拥有比父类更为严格的访问控制权限 |
super关键字:
1.super用于方法
a.用于构造方法
表示调用父类构造方法。
语法:super (参数列表)
I. 当子类调用父类无参构造时,super( )可写可不写,表示调用父类无参构造。
II. 当子类调用父类有参构造时,super(参数列表)必须要写,要告诉编译器当前调用的是哪个有参构造。
注意:
(1)子类构造方法中调用父类构造必须是第一行语句
(2)this与super不能同时调用
b.用于普通方法
语法:super.方法名(参数)
用于在子类中明确调用父类被覆写的方法。
举个栗子:
class Person{
private String name;
public Person(String name){
this.name = name;
System.out.println("父类的有参构造");
}
public void print(){
System.out.println("父类的print方法");
}
}
class Student extends Person{
public Student(){
super("li");//super用于构造方法
System.out.println("子类的无参构造");
}
public void print(){
super.print();//super用于普通方法
System.out.println("子类的print方法");
}
}
public class Test{
public static void main(String[] args){
new Student().print();
}
}
运行结果:
2.super用于属性
super.属性名
表示调用父类中被覆写的属性
final关键字:
1.final修饰类(String类以及8大基本数据类型的包装类,Integer)
a.当一个类被final关键字修饰,表示该类不能拥有子类(该类不允许被继承)。
b.一旦一个类被final修饰,该类的所有方法都会默认加上final。
2.final修饰方法
当一个方法被final修饰,明确表示该方法不能被覆写。
3.final修饰变量
I.final修饰普通数据类型的成员变量
被final修饰的成员变量必须在声明时初始化(构造块或构造方法中初始化),并且初始化后值无法被修改。
II.final修饰引用数据类型的变量
其引用不可变,即不能再指向其他的对象。
多态性:
1.方法的多态性
a.方法的重载:同一个方法名称可以根据参数的类型或个数不同,调用不同的方法体。
b.方法的覆写:同一个父类的方法,可能根据实例化子类的不同也有不同的实现。
2.对象的多态性
a.对象的向上转型:用于参数统一化
语法:父类 父类引用 = new 子类();
不管是否发生了向上转型,其核心本质还在于:你使用的是哪一个子类(new在哪里),而且调用的方法是否被子类覆写了。
b.向下转型:当父类引用需要调用子类扩充方法时,才需要向下转型
语法:子类 子类引用 = (子类)父类引用;
注:要发生向下转型,必须先发生向上转型
举个栗子:
class Person{
public void print(){
System.out.println("我是父类方法");
}
}
class Student extends Person{
public void print(){
System.out.println("我是子类方法");
}
public void fun(){
System.out.println("只有子类有");
}
}
public class Test{
public static void main(String[] args){
Person per = new Student();//向上转型
per.print();
System.out.println("-----------------------");
//这个父类能够调用的方法只能是本类定义好的方法
//所以并没有子类中的dun()方法,那么只能向下转型
Student stu = (Student) per;//向下转型
stu.fun();
}
}
运行结果:
内部类的定义与使用:
内部类:在类内部进行其他类结构嵌套操作。
1.内部类的优缺点:
内部类的优点:
a.内部类与外部类可以方便的访问彼此的私有域(包括私有方法,私有属性)
b.内部类是另外一种封装(保护性),对外部的其他类隐藏
c.内部类可以实现Java单继承的局限
缺点:
结构复杂
2.内部类与外部类的关系:
a.对于非静态内部类,内部类的创建需要依赖外部类对象,在没有外部类实例之前无法创建非静态内部类。
b.内部类是一个相对独立的个体,与外部类没有is--a关系。
c.内部类可以直接访问外部类的元素(包含私有域),但是外部类不可以直接访问内部类元素,需要通过内部类的引用间接访问。
3.创建内部类语法
a.在外部类外部创建非静态内部类
外部类.内部类 内部类引用 = new 外部类().new 内部类();
Outter.Inner in = new Outter( ).new Inner( );
b.在外部类外部创建静态内部类
外部类.内部类 内部类引用 = new 外部类.内部类( );
Outter.Inner in = new Outter.Inner( );
4.内部类的分类
I.成员内部类(类比成员方法)
a.成员内部类内部不能存在任何static变量或方法,可以访问外部类的静态域
b.成员内部类是依附外部类的,所有只有先创建了外部类,才能创建内部类。
II.静态内部类(类比静态方法)
a.静态内部类的创建不需要依赖外部类,可以直接创建
b.静态内部类不可以使用任何外部类的非static域(包含属性与方法),但是可以存在自己的成员变量
举个栗子:
class Outter{
private static String msg = "Hello World";
//定义一个内部类
static class Inner{
public void print(){
//只能使用外部类的静态属性
System.out.println(msg);
}
}
//在外部类定义一个方法,负责产生内部类对象并且调用print方法
public void fun(){
Inner in = new Inner();//内部类对象
in.print();//内部类提供的print方法
}
}
public class Test{
public static void main(String[] args){
Outter.Inner in = new Outter.Inner();
in.print();
}
}
运行结果:
III.方法内部类(局部内部类)
a.方法内部类不允许使用访问权限修饰符 public、private、protected
b.方法内部类对外部完全隐藏,除了创建这个类的方法可使用以外,其他地方均不能访问
c.方法内部类如果要想使用方法形参,该形参必须使用final声明(JDK8将形参变为隐式final声明)
IV.匿名内部类(lamdba表达式前身)
匿名内部类就是一个没有名字的方法内部类。因此特点与方法内部类完全一样,除此之外还有两个自己的特点:
a.匿名内部类必须继承一个抽象类或者实现一个接口
b.匿名内部类没有构造方法,因为它没有类名
举个栗子:
interface MyInterface{
void test();
}
class Outter{
private int num;
public void display(int para){
//匿名内部类,实现了MyInterface接口
new MyInterface(){
@Override
public void test() {
System.out.println("匿名内部类"+para);
}
}.test();
}
}
public class Test{
public static void main(String[] args){
Outter outter = new Outter();
outter.display(20);
}
}
运行结果:
5.内部类的特点
a.破坏了程序的结构
b.方便进行私有属性的访问。(外部类也可以访问内部类的私有结构)
c.如果发现类名称出现了“.”,应当 立即想到内部类的概念