续封装内容:
代码块:
在Java中,被{}括起来的代码称之为代码块,根据其位置与声明的不同,分为四个代码块:
局部代码块, 构造代码块,静态代码块,同步代码块。这里只讲解前三种,同步代码块位于多线程讲解。
局部代码块:在方法中出现,限定变量的生命周期,及早释放,提高内存的使用。
在同一个类中的同一个方法中,如果存在多个局部代码块,执行顺序是自上而下的。
构造代码块:(在创建对象的时候调用)
定义在类中方法外,当一个类中既存在构造代码块也存在构造的方法的时候,在创建对象时,会先执行构造代码块,后执行构造方法。无论类中有多少个构造代码块,构造代码块之间会先进行自上而下的顺序执行,然后在执行构造方法。
静态代码块:
在类中方法外定义,并加上static修饰,静态代码块优先于其他所有代码块之前执行。
静态代码块在加载的时候执行,且只执行一次。
代码中执行顺序:
静态代码块--------局部代码块--------构造代码块--------构造方法
class Student2{
static {
System.out.println("这是在Student2类中静态代码块"); // 1
}
{
System.out.println("这是在Student2类中的构造代码块"); // 2
}
Student2(){
System.out.println("这是Student2类中的无参构造方法");// 3
}
}
//执行顺序: 4 5 6 1 2 3
public class CodeTest {
static {
System.out.println("这是在CodeTest类中的静态代码块"); // 4
}
public static void main(String[] args) {
System.out.println("开始执行main方法"); // 5
{
System.out.println("这是在CodeTest中的局部代码块"); // 6
}
Student2 student2 = new Student2();
}
}
输出结果:
继承:
先给出一段代码,观察:
class Father{
String name;
int age;
Father(){}
//getXxx()/setXxx()
public void study(){
System.out.println("学习");
}
}
class Son{
String name;
int age;
Son(){}
//getXxx()/setXxx()
public void study(){
System.out.println("学习");
}
}
发现两个类中的name与age都是相同的,如果一个需求需要一百个这样的代码,显得很麻烦,于是解决办法是将这些相同的代码集合到同一个类中,并且使得father类与son类与其产生一个关系,通过这个关系可以具备这个类中所定义的功能与属性,这就解决了代码的冗余率
Java提供了一个思想用来实现这一办法:继承
简单理解继承就是儿子需要继承父亲的公司,公司的所有物儿子在继承之后儿子也会拥有。
格式:class 子类名 extends 父类名{。。。}
由此可见要实现继承至少需要2个类:class Zi extends Fu{。。。}
这里的Zi称之为:子类,派生类;Fu称之为:父类,基类,超类。
继承定义:把多个类相同的内容提取到另外一个类中,然后使用关键字extends来实现继承
继承的好处:
1、提高了代码的复用性,只需要看父类即可。
2、提高了代码的维护性,当需要修改时只需要修改父类中的内容即可
3、让类与类之间产生了继承关系,为多态做铺垫(有继承才能有多态)。
坏处:类的耦合性增强
所谓耦合就是类与类之间的关系(一荣俱荣,一损俱损)。
与之相对的内聚就是指类自己本身可以完成的功能,
开发原则一般为:高内聚,低耦合
Java中继承的特点:
1、Java中的类class只支持单个继承,不允许多个继承
2、Java中的类支持多层继承(形成了一个继承体系)
class GrandDad{
public void show(){
System.out.println("我是爷爷");
}
}
class Father extends GrandDad{
public void show1(){
System.out.println("我是父亲");
}
}
class Son extends Father{
public void show2(){
System.out.println("我是儿子");
}
}
public class Demo{
public static void main(String[] args){
Son s = new Son();
s.show();//可以使用爷爷的方法
s.show1();//可以使用父亲的方法
s.show2();//可以使用自己的方法
}
}
输出结果就是三行不同的话。但是不允许如下这样写:
class Son extends Father,Mother{},这也就是不允许多个继承。
什么时候使用继承:当类与类之间满足is a(什么是什么)的关系时,可以使用
使用继承时的注意事项1:
1、要想初始化子类,必须要先初始化父类,即先有父亲才有儿子
2、子类只能继承父类的非私有成员(即成员变量和成员方法)
3、子类不能继承父类的构造方法,但是可以使用super关键字访问父类的构造方法。
要想初始化子类,就必须要先初始化父类,因为子类会继承父类的数据,甚至会使用父类的数据,所以在子类初始化之前一定会先完成父类的初始化,而初始化父类就是通过构造方法进行初始化的
注意:每个子类的构造方法的第一句话默认是super(),即访问父类中的无参构造方法,对父类进行初始化
4、不建议为了部分相同的功能而去使用继承。
继承的注意事项2:
1、当子类中的成员变量与父类成员变量相同时,查找:(就近原则)
1)先在方法的局部范围内查找,如果找到就返回。
2)若在方法的局部范围内找不到,去本类的位置上查找,如果找到就返回
3)若在本类中成员位置上找不到,去父类中的成员位置上查找,如果找到就返回。
4)若父类中的成员位置上找不到,报错
2、当子类中的成员变量与父类中的成员变量不同时,使用什么变量名就访问谁
class Father{
int num = 10;
int num2 = 200;
public void show2(){
int num = 100;
int num2 = 20;
}
}
class Son extends Father{
int num = 30;
int num3 = 300;
public void show1(){
// int num = 40;
System.out.println(num);
System.out.println(num2);
System.out.println(num3);
}
}
public class ExtendsDemo {
public static void main(String[] args) {
Son son = new Son();
son.show1();
}
}
输出结果为:
若是不仅要输出局部范围内的num,还要输出父类中的num,但是在子类成员变量与父类相同时,是使用就近原则进行查找的,此时Java提供了一个关键字使得可以直接访问父类中的成员变量,这个关键字就是上文提到的:super
super与this之间的区别:
this代表调用该类的当前对象;super代表的是父类存储空间的标识(父类的引用,可以操作父类的成员)
如何使用:
1、访问成员变量:
this.成员变量 访问的是本类中的成员变量
super.成员变量 访问的是父类中的成员变量
2、访问构造方法:
this(。。。);super(。。。)
3、访问成员方法:
this.成员方法();super.成员方法();
class Father7{
int num = 10;
public void show2(){
System.out.println("这是父类中的show2方法");
}
}
class Son7 extends Father7{
int num = 20;
public void show(){
int num = 30;
System.out.println(num);
System.out.println(this.num); // 访问的是本类中的成员变量
System.out.println(super.num);// 访问的是父类中的成员变量
show2();//通过继承,可以访问父类中的方法
super.show2();//访问父类中的成员方法
show3();
this.show3();
}
public void show3(){
System.out.println("这是Son7类中的show3方法");
}
}
public class ExtendsDemo6 {
public static void main(String[] args) {
Son7 son7 = new Son7();
son7.show();//调用Son7中的show()方法
}
}
final关键字:可以修饰成员变量,成员方法,类
修饰类:类不可以被继承
修饰成员变量:变量变成自定义常量
修饰成员方法:方法不可被重写
final修饰变量的初始化时机:在对创建对象的构造方法执行完毕前初始化即可。
上文提到每个子类的构造方法第一句,默认为super(),意为访问父类的无参构造方法,若是父类中只有有参的构造方法,没有无参构造方法,如何解决?
1、使用super关键字带参形式访问父类中的带参构造方法
2、子类使用this关键字访问本类中的其他构造方法,但是要注意:本类中的其他构造方法也必须是可以访问到父类拥有的构造方法,使用this关键字间接的调用父类的构造方法,无论在哪里调用父类的构造方法,只要保证在子类构造方法内容执行之前完成了对父类的初始化就可以了
3、在使用this(。。。)和super(。。。)时,必须出现在第一条语句上,否则会出现父类数据进行多次初始化(每个类只能初始化一次)
class Father{
int age;
Father(String s){
System.out.println("这是父类的带参构造方法"+s);
}
}
//子类
class Son{
Son(){
super("你好")
System.out.println("这是子类的无参构造方法");
}
Son(String s){
this();
// 这里若是加入:super(),会报错,因为不在第一句
System.out.println("这是子类带参数的构造方法"+s);
}
}
class Demo{
public static void main(String[] args){
Son s = new Son("你也好");
}
}
输出结果:
继承与成员方法的关系同继承与成员变量的关系一样,相同时先在本类中查找,再去父类中找,最后若是父类没有,则报错
当子类的方法名与父类方法的声明相同时,这种情况叫做方法的重写,重写发生在继承关系中、
重写与重载的区别:
class OldPhone{
public void call(String name){
System.out.println("给"+name+"打电话");
}
}
class newPhone extends OldPhone{
@Override
public void call(String name) {
super.call(name);
System.out.println("看抖音");
}
}
public class ExtendsDemo10 {
public static void main(String[] args) {
newPhone newPhone = new newPhone();
newPhone.call("王宇");//这里调用的是子类的方法
}
}
重写是发生在继承的关系的关系中,重载是发生在本类中。
重载是方法名一致,参数列表不一致就是重载。
重写是方法名,参数列表,返回值都一样,实现不一样,叫方法的重写。
重载的英文单词:overload 重写的英文单词:override
方法重写的注意事项:
1、父类中私有的方法不能被重写
2、子类重写父类的方法时候,访问权限不能更低 要么子类重写的方法访问权限比父类的访问权限要高或者一样 建议:以后子类重写父类的方法的时候,权限修饰符写一样就不会发生这样的问题。
3、父类中静态的方法不能被重写,也就是说不能被Override修饰,因为静态的是属于类本身的东西。
一个类的初始化过程:
1、栈开辟空间
2、堆开辟空间给对象
3、成员变量的值是系统默认值
4、成员变量显式赋值
5、构造方法赋值
如下代码结合代码块为例考察执行顺序:
//测试
class X {
Y b = new Y(); // 1
X() {
System.out.print("X"); // 3
}
{
System.out.print("S");
}
Y d = new Y();
}
class Y {
Y() {
System.out.print("Y"); //2
}
}
class Z extends X{
Y y = new Y();
static{
System.out.print("C");
}
Z(){
// super(); //这里的super()存在的意义不大 因为已经初始化过了。
System.out.println("Z");
}
}
public class ExtendsTest2{
public static void main(String[] args) {
new Z();
}
}
执行流程:
1、首先执行主函数main(),main()中通过Z无参构造方法初始化,进入到类Z中
2、在类Z进行加载的时候,静态代码块运行,输出C,然后进入类Z中
3、类Z中想要初始化,就必须要先初始化其父类,因此进入到类X中
4、X中执行第一行代码,创建Y对象b并初始化,因此进入到类Y中
5、Y中调用Y无参构造方法,输出Y,执行完毕返回类X
6、执行X中的构造代码块,输出S,再进入到Y中输出Y,最后执行无参构造方法,输出X,执行完毕X初始化完成,返回子类Z
7、Z中执行第一行代码再次进入到类Y中输出Y,然后返回类Z,执行无参构造方法,输出Z,至此结束
8、最终输出:CYSYXYZ