类和对象(2)
一、代码块
代码块:
使用{ }定义的一段代码。可分为普通代码块、构造快、静态块、同步代码块。
(1)普通代码块:定义在方法中的代码块。(一般方法中代码过长,为避免变量的命名冲突,就使用普通代码块)
class Test5{ public static void main(String[] args) { //以下为直接使用{}定义的普通代码快 { String month = "April"; System.out.println("month = "+ month); } String month = "March"; System.out.println("month = "+ month); } } |
(2)构造块
定义在类中的代码块,不加任何修饰符。
构造块优先于构造方法执行,每产生一个对象就调用一次构造块,在调用构造方法前构造块可进行简单的逻辑操作。
class Demo{ //定义在类中不加任何修饰符为构造块 { System.out.println("我是Demo类的构造块"); } public Demo(){ System.out.println("我是Demo类的构造方法"); } } public class Test5{ public static void main(String[] args) { Demo demo = new Demo(); Demo demon1 = new Demo(); } } |
(3)静态代码块
静态代码块:使用static定义的代码块。
根据代码块所处的位置不同可分为:在非主类中的静态代码块和在主类中的静态代码块。
A.在非主类中定义的静态代码块
静态块的作用:为static属性进行初始化
非主类中的静态代码块优先于构造块执行,且无论实例化多少次对象,静态代码块只执行一次。
class Demo{ { //Dem类的构造块 System.out.println("我是Demo类的构造块"); } public Demo(){ System.out.println("我是Demo类的无参构造方法"); } static{ //Demo类的非主类静态构造块 System.out.println("我是Demo类的非主类静态构造块"); } } public class Test5{ public static void main(String[] args) { Demo demo = new Demo(); Demo demon1 = new Demo(); } } |
B.在主类中定义的代码块
定义在主类中的代码块,静态块优先于主方法执行,且无论实例化多少次对象静态代码块只运行一次。
执行顺序: 静态块->主方法开始->构造块->构造方法->主方法结束。
//在主类中的代码块 public class Test5{ { //主类的构造块 System.out.println("我是主类的构造块"); } public Test5(){ //构造方法 System.out.println("我是主类的无参构造方法"); } static{ //主类的静态构造块 System.out.println("我是主类的静态构造块"); } public static void main(String[] args) { System.out.println("我是main的开始"); new Test5(); new Test5(); System.out.println("我是main的结束"); } } |
二、内部类:
1、内部类的基本概念:
就是一个类的内部嵌套其他类的操作。
内部类的引入,使得程序的结构有些混乱,但可以方便的操纵外部类的私有属性。如下例:
class Out{ private String name = "lemon"; //定义内部类 class In{ //在内部类中定义myPrint()方法 public void myPrint(){ //打印name的值 System.out.println(name); } } //在外部类中定义一个print()方法 public void print(){ In in = new In();//内部类对象 in.myPrint();//内部类提供的方法 } } public class Test5{ public static void main(String[] args) { Out out = new Out();//实例化外部类 out.print();//调用外部类的方法 } } |
若不引入内部类不改变主方法实现例的功能。
class Out{ //Out类的私有属性 private String name = "lemon"; //提供name的getter方法 public String getName(){//(9) return this.name; } //Out类的print方法 public void print(){//(3) In in = new In(this);//this表示当前对象(4) in.myPrint();//调用In类的myPrint()方法(7) } } class In{ private Out out; public In(Out out){//(5) //In.out = main.out this.out = out;//引用传递(6) } public void myPrint(){//(8) //In类的myPrint()方法 System.out.println(out.getName());//(10) } } public class Test5{ public static void main(String[] args) { Out out = new Out();//实例化外部类(1) out.print();//调用外部类的方法(2) } } |
内部类使用注意事项:
内部类的访问必须通过外部类的方法,如果要想在程序外部调用,则应按以下格式对内部类进行实例化创建(必须先进行外部类的实例化,因为此时外部类中存在普通属性,这些属性必须实例化后才可使用):
外部类 . 内部类 内部类对象 = new 外部类(). new 内部类();
如果一个内部类只想被外部类使用(不希望产生内部类的实例化对象),此时可以用private来修饰内部类。
在进行属性访问时都要加this关键字,如果想要在内部类中明确使用this,应按如下语法:
外部类.this.属性
则表示外部类当前对象的属性
2、static定义的内部类
若内部类使用static进行定义,则该类只允许访问外部类中的static操作。(相当于外部类)
定义方法:外部类.内部类 内部对象 = new 外部类.内部类();
class Out{ private static String name = "lemon"; //定义static修饰的内部类 static class In{ //在内部类中定义myPrint()方法 public void myPrint(){ //打印name的值 System.out.println(name); } } } public class Test5{ public static void main(String[] args) { Out.In in = new Out.In();//实例化外部类 in.myPrint();//调用外部类的方法 } } |
3、在方法中定义的内部类
在JDK1.7及以前,如果一个内部类定义在方法之中,该内部类如果想访问方法中的参数,那么这个参数前必须使用 final关键字修饰。
class Out{ //外部类的静态私有变量 private static String name ="lemon"; //定义外部类的print方法 public void print(int num){ //方法中的内部类 class In{ //定义内部类的myprint方法 public void myPrint(){ System.out.println("num = "+ num); System.out.println("name = "+ name); } } new In().myPrint();//产生内部类对象并调用方法 } } public class Test5{ public static void main(String[] args) { Out out = new Out();//实例化外部类 out.print(66);//调用外部类的print方法 } } |
综上所述内部类的特点:
1、破坏了程序的结构
2、方便访问外部类的私有属性,同时外部类也可以访问内部类的私有属性。
3、如果发现类名称上出现了".",应当立即想到内部类的概念。
三、继承的定义与使用
继承(面向对象的第二大特征):
主要作用在与,在已有的基础上继续进行功能的扩充。
class Person{ private String name; private int age; public void setName(String name){ this.name = name; } public String getName(){ return name; } public void setAge(int age){ this.age = age; } public String getAge(){ return age; } } class Student{ private String name; private int age; private String school; public void setName(String name){ this.name = name; } public String getName(){ return name; } public void setAge(int age){ this.age = age; } public int getAge(){ return age; } public void setSchool(String school){ this.school= school; } public String getSchool(){ return school; } } |
以上的程序就是我们之前一直使用的模式,单独的Java类,含有大量的重复代码,不仅代码重复,而且从某种意义上来讲,学生就属于人,只不过比人更加具体,描述的范围更小,具有的属性及方法更多。
所以为了结构定义上的重复,我们就可以使用到继承。
1、继承的实现
使用extends关键字来实现(子类也被称为派生类,父类被称为超类)
class子类extends父类
在发生了类继承关系后,子类可以直接继承父类的操作,可实现代码的重用。子类最少也维持和父类相同的功能,且子类也可进行功能的扩充(扩充方法和属性)
//子类继承父类与父类具有相同的功能 class Person{ private String name; private int age; public void setName(String name){ this.name = name; } public String getName(){ return name; } public void setAge(int age){ this.age = age; } public int getAge(){ return age; } } class Student extends Person{ } public class Test5{ public static void main(String[] args) { Student stu = new Student(); stu.setAge(20); stu.setName("demon"); System.out.println("stu's name: "+stu.getName()+" ,stu's age: "+stu.getAge()); } } |
//子类对父类进行属性和方法的扩充 class Person{ private String name; private int age; public void setName(String name){ this.name = name; } public String getName(){ return name; } public void setAge(int age){ this.age = age; } public int getAge(){ return age; } } class Student extends Person{ private String school = "陕西科技大学"; public String getSchool(){ return school; } public void print(){ System.out.println("我对父类进行的属性和方法的扩充"); } } public class Test5{ public static void main(String[] args) { Student stu = new Student(); stu.setAge(20); stu.setName("demon"); System.out.println("stu's name: "+stu.getName()+" ,stu's age: "+stu.getAge()); System.out.println(stu.getSchool()); stu.print(); } } |
继承的主要作用:对类进行功能的扩充,及解决代码的重用。
2、继承的限制(Java中只允许单继承)
子类对象在实例化前一定会先实例化父类对象,默认调用父类的构造方法后在调用子类的构造方法进行子类对象实例化。
实际子类的构造方法中隐含了一个super();
若父类没有提供无参构造函数,此时必须使用super()明确指明要调用的父类构造函数。
class A{ public A(){ System.out.println("我是父类产生的对象"); } } class B extends A{ public B(){ //super();//该语句在父类为无参构造时可写可不写 System.out.println("我是子类产生的对象"); } } public class Test5{ public static void main(String[] args) { B b = new B(); } } |
错误的多重继承: class A{} class B{} class C extends A,B{} | 正确方式(多层继承实现多重继承) class A{} class B extends A{} class C extends B{} |
继承总结:
Java只允许单继承,不允许多继承。
在进行继承的时候,子类会继承父类的所有结构(包含私有属性、构造方法、普通方法)。但是这个时候需要注意的是,所有的非私有操作属于显示继承(可以直接调用),所有的私有操作属于隐式继承,可用getter和setter方法形式调用。
显式继承与隐式继承
隐式继承:子类能够使用所有的非private属性,而所有的private属性无法被直接使用。
class A{ private String name; public int age = 18; public void setName(String name){ this.name = name; } public String getName(){ return name; } } class B extends A{ public void print(){ System.out.println(getName()); } } public class Test5{ public static void main(String[] args) { B b = new B(); System.out.println(b.age); b.setName("demon"); System.out.println(b.getName()); b.print(); } } |
四、覆写(override)
覆写:子类定义了和父类相同的方法或属性,这样的操作称为覆写或重写。
1、方法的覆写
方法的覆写:子类定义了与父类方法名称、参数类型和个数完全相同的方法。但是被覆写不能拥有比父类更为严格的访问控制权限。
class A{ public void print(int num){ System.out.println("我是父类的方法"); } } class B extends A{ public void print(int a){ System.out.println("我是子类的方法"); } } public class Test5{ public static void main(String[] args) { B b = new B(); b.print(18); } } | 错误代码: class A{ public void print(int num){ System.out.println("我是父类的方法"); } } class B extends A{ default void print(int a){ System.out.println("我是子类的方法"); } } public class Test5{ public static void main(String[] args) { B b = new B(); b.print(18); } }
|
若父类的方法为private修饰,子类可否能用public覆写该方法?
class A{ public void fun(){ this.print(); } //如果父类的方法使用了private修饰,那么表示该方法只能被父类使用, //而不能被子类使用(即子类不知道该方法的存在性,所以也不会出现覆写问题) private void print(){ System.out.println("我是父类的方法"); } } class B extends A{ //这个时候该方法只是子类定义的新方法而已,和父类的方法有任何关系。 public void print(){ System.out.println("我是子类的方法"); } } public class Test5{ public static void main(String[] args) { B b = new B(); b.fun(); } }
|
2、属性的覆写
当子类定义了和父类属性名称完全相同的属性的时候,就称为属性的覆写。
class Person{ public String info = "Person"; } class Student extends Person{ // 按照就近取用原则,肯定找被覆盖的属性。 public String info = "Student"; } public class Test5{ public static void main(String[] args) { System.out.println(new Student().info); } } |
这种操作基本上毫无意义,原因在于:类中的属性都要求使用private封装,一旦封装了,子类不知道父类具有什么属性,那么也就不存在属性覆盖的问题了。
重载(overload)和覆写(override)的区别
3、super关键字
在上述的子类对象实例化操作中使用super()主要是子类调用父类的构造函数,所以在覆写中,子类也可以使用super.方法()或super.属性明确调用父类的方法或属性。
(1)使用super调用父类的同名方法
class A{ public void fun(){ this.print(); } public void print(){ System.out.println("我是父类的方法"); } } class B extends A{ public void print(){ super.print(); System.out.println("我是子类的方法"); } } public class Test5{ public static void main(String[] args) { B b = new B(); b.print(); } } |
(2)使用super调用父类属性
class A{ public String msg = "我是父类"; public void fun(){ this.print(); } public void print(){ System.out.println("我是父类的方法"); } } class B extends A{ public String msg = "我是子类"; public void print(){ System.out.println(super.msg);//super调用父类的属性 System.out.println(this.msg); super.print();//super调用父类的方法 System.out.println("我是子类的方法"); } } public class Test5{ public static void main(String[] args) { B b = new B(); b.print(); } } |
可以发现super和this在使用上非常的相似,但是两者最大的区别是super是子类访问父类的操作,而 this是本类的访问处理操作。
Super和this的区别
六、Final关键字
在Java中final被称为终结器,可以用final定义类、方法、属性。
使用final定义的类不能有子类;
final class A{} //A类不能有子类
使用final定义的方法不能被子类方法所覆写;
class A{ public final void fun(){} }
使用final定义的变量就成为了常量,常量必须在声明时赋值,并且不能够被修改。(定义常量全部用大写字母,且多个单词中间用_分隔)
publc final int LEVEL_A = 100 ;
七、多态性
Java中多态的核心表现为:
1、方法的多态性
①方法的重载:同一个方法名称可以根据参数的类型或个数不同调用不同的 方法体。
②方法的覆写:同一个父类的方法,可能根据实例化子类的不同也有不同的实现。
2、对象的多态性(前提:方法的覆写)
①对象的向上转型:父类 父类对象 = 子类实例。
②对象的向下转型(强制转型):子类 子类对象 = (子类)父类实例。
向上转型:
不管是否发生了向上转型,核心本质还是在于:你使用的是哪一个子类(new在哪个类),而且调用的方法是否被子类所覆写了。
//向上转型 class Person{ public void print(){ System.out.println("我是蜘蛛女孩"); } } class Student extends Person{ public void print(){ System.out.println("我是小仙女"); } } public class Test5{ public static void main(String[] args) { Person per = new Student();//向上转型 per.print(); } } | //向下转型 class Person{ public void print(){ System.out.println("我是蜘蛛女孩"); } } class Student extends Person{ public void myPrint(){ System.out.println("haha"); } public void print(){ System.out.println("我是小仙女"); } } public class Test5{ public static void main(String[] args) { Person per = new Student();//向上转型 per.print(); //这个时候父类能够调用的方法只能是本类定义好的方法 //所以并没有Student类中的myPrint()方法,那么只能够进行向下转型处理 Student stu = (Student)per;//向下转型 stu.myPrint(); } } |
但不是所有的父类对象都可以进行向下转型:如果想进行向下转型,在之前必须先经过向上转型;否则在转型时会出现ClassCastException
且向下转型存在安全性隐患,所以我们应该先判断再进行转型,要依赖于关键字instanceof类,返回boolean类型。
class Person{ public void print(){ System.out.println("我是蜘蛛女孩"); } } class Student extends Person{ public void myPrint(){ System.out.println("haha"); } public void print(){ System.out.println("我是小仙女"); } } public class Test5{ public static void main(String[] args) { Person per = new Student(); System.out.println(per instanceof Person); System.out.println(per instanceof Student); if (per instanceof Student) { //避免ClassCastException Student stu = (Student) per ; stu.myPrint(); } } } |
多态性总结:
对象多态性的核心在于方法的覆写。
通过对象的向上转型可以实现接收参数的统一,向下转型可以实现子类扩充方法的调用。
两个没有关系的类对象是不能够进行转型的,一定会产生ClassCastException。