代码块、类的继承
文章目录
一、代码块
1、代码块的定义
在Java中,使用{}括起来的代码被称为代码块
2、分类
根据其位置和声明的不同,可以分为局部代码块,构造代码块,静态代码块,同步代码块(多线程讲解)。
局部代码块:
在方法中出现,限定变量的生命周期,及早释放,提高内存的利用
在同一个类中的同一个方法中,如果存在多个局部代码块,执行顺序是自上而下的。
构造代码块:(在创建对象的时候调用)
定义在类中方法外。当一个类中既存在构造代码块也存在构造方法的时候,在创建对象时,会先执行构造代码块,后执行构造方法。
无论类中有多少个构造代码块,构造代码块之间会先进行自上而下的顺序执行,然后再执行构造方法。
构造代码块 -- 构造方法
静态代码块:
在类中方法外定义,并加上static修饰。
public class CodeDemo {
/**
* 静态代码块
*/
static {
int a = 22;
System.out.println(22);
}
CodeDemo(){
int y = 300;
System.out.println(y);
}
/**
* 定义在类中方法外,使用大括号括起来的代码,称之为构造代码块
*/
{
int x = 200;
System.out.println(x);
}
{
int x = 400;
System.out.println(x);
}
{
int x = 500;
System.out.println(x);
}
/**
* 静态代码块
*/
static {
int a = 11;
System.out.println(a);
}
public static void main(String[] args) {
/**
* 局部代码块
*/
{
int x = 10;
System.out.println(x);
}
// System.out.println(x);
{
int y = 20;
System.out.println(y);
}
{
int z = 30;
System.out.println(z);
}
CodeDemo codeDemo = new CodeDemo();
CodeDemo codeDemo1 = new CodeDemo();
// {
// int z = 40;
// System.out.println(z);
// }
}
}
从运行结果可以看出 静态代码块是优先于其他代码块之前执行,
在加载的时候执行,并且只执行一次。
静态代码块 -- 局部代码块 -- 构造代码块 -- 构造方法
二、类的继承
1、引入继承的思想
首先我们写一个完整的类
public class Student {
//成员变量
//姓名
String name;
//性别
String gender;
//年龄
int age;
//定义成员方法
//学习
public void study(){
System.out.println("我热爱学习大数据");
}
//吃饭
public void eat(){
System.out.println("学习饿了,吃会东西");
}
//睡觉
public void sleep(){
System.out.println("学习类,睡一会觉");
}
}
class StudentTest{
public static void main(String[] args) {
// System.out.println("HelloWorld");
//如果不创建对象能不能使用呢?(指的是能否访问到成员变量或者成员方法)
// System.out.println(name);
//创建一个学生对象才能使用
Student s1 = new Student();
//这样我们就创建出一个学生对象s1
//如何通过对象访问成员变量呢?
//访问格式:对象名.成员变量名
//访问s1对象的姓名是什么
// s1.name; //访问到的变量值不能直接使用,应该用一个变量接收或者直接使用输出
System.out.println(s1.name); //null
System.out.println(s1.gender); //null
System.out.println(s1.age); //0
//如何给成员变量赋值
s1.name = "王宇";
s1.gender = "男";
// s1.age = "明旺 "; //注意,赋值的时候,注意数据类型
s1.age = 18;
System.out.println(s1.name);
System.out.println(s1.gender);
System.out.println(s1.age);
//如何去调用成员方法呢?
//调用格式:对象名.成员方法名(...);
s1.study();
s1.eat();
s1.sleep();
}
}
通过观察我们发现,name和age这两个成员变量,以及study()学习方法都是相同的
如果我们还继续定义其他的儿子,其他的父亲等等这些类,每定义一次这样的类,是不是就要把这些重复的内容再写一遍。
感觉这样很麻烦。怎么去改进呢?
能不能把这些相同的内容定义到一个独立的类中,然后,让其他的类与这个独立的类产生一个关系,建立好这个关系之后呢,
这些其他的类就具备了这个独立的类的功能。
为了实现这样的基数,java提供了一个技术手段给我们使用,也是一个思想:继承
父亲:
2个儿子
继承如何使用代码表示呢?
代码的格式是什么样子呢?
语句格式:class 子类名 extends 父类名{...}
要实现一个继承关系至少需要2个类
class Fu{}
class Zi extends Fu{}
class Zi extends Fu{}
这里的Zi我们称之为子类,派生类,
这里的Fu我们称之为父类,超类,基类
2、继承
package com.shujia.wxl.day10;
import com.shujia.wxl.day08.Teacher;
import com.sun.corba.se.impl.legacy.connection.USLPort;
/*
继承:
把多个类相同的内容提取到另外一个类中,然后使用关键字extends来实现继承
如何实现:
利用java提供的关键字:extends来实现继承
语句格式:
class 子类名 extends 父类名{}
继承的好处:
1、提高了代码的复用性 看一看父类
2、提高了代码的维护性 只需要修改父类的内容
3、让类与类之间产生了继承关系,为了后面多态做铺垫(要有继承才能有多态)
继承的坏处:
1、类的耦合性增强了。(内聚)
开发的原则:
低耦合,高内聚
耦合:类与类之间的关系
内聚:类自己本身可以完成的事情
*/
//class Student3{
// String name;
// int age;
//
// Student3(){}
//
// //getXxx()/setXxx()
//
// public void eat(){
// System.out.println("吃饭");
// }
//
// public void study(){
// System.out.println("学习");
// }
//}
//
//class Teacher3{
// String name;
// int age;
//
// Teacher3(){}
//
// //getXxx()/setXxx()
//
// public void eat(){
// System.out.println("吃饭");
// }
//
// public void study(){
// System.out.println("学习");
// }
//}
class Person{
String name;
int age;
Person(){}
//getXxx()/setXxx()
public void eat(){
System.out.println("吃饭");
}
public void study(){
System.out.println("学习");
}
}
//学生类继承人类之后,拥有了人类中的成员变量,成员方法,也可以自己定义自己类特有的成员变量和成员方法
class Student3 extends Person{
String Id;
public void playGame(){
System.out.println("打游戏");
}
}
class Teacher3 extends Person {
}
public class ExtendsDemo2 {
public static void main(String[] args) {
//创建一个学生对象并使用
Student3 s1 = new Student3();
s1.eat();
s1.study();
s1.playGame();
//创建一个老师对象
Teacher3 t1 = new Teacher3();
t1.eat();
t1.study();
}
}
3、特点
class GrandFather{
public void show(){
System.out.println("我是爷爷");
}
}
class Father4 extends GrandFather{
public void show1(){
System.out.println("我是老子");
}
}
class Son4 extends Father4{
public void show2(){
System.out.println("我是儿子");
}
}
public class ExtendsDemo3 {
public static void main(String[] args) {
Son4 son4 = new Son4();
son4.show2(); //可以使用自己的方法
son4.show1(); //也可以使用父亲的方法
son4.show(); //也可以使用爷爷的方法
}
}
4、注意事项一
使用继承时所需要的注意事项:
1、要想初始化子类,必须先初始化父类
举例:现有父亲,才能有儿子
2、子类只能继承父类的非私有的成员(成员变量和成员方法)
3、子类不能继承父类的构造方法,但是可以通过super关键字去访问父类的构造方法
因为要想初始化子类,必须先初始化父类,是通过构造方法进行初始化的。
4、不要为了部分的功能而去使用继承
class A{
public void fun1(){}
public void fun2(){}
}
class B{
public void fun1(){}
public void fun3(){}
public void fun4(){}
public void fun5(){}
public void fun6(){}
}
部分同学认为我学过了继承,就必须要使用继承,通过观察发现B类中有一个方法和A类中的方法一样
用继承实现
class B extends A{
public void fun3(){}
public void fun4(){}
public void fun5(){}
public void fun6(){}
}
这样并没有提高太大的效率,不推荐!!
那么,什么时候使用继承呢?
在英语中有一个语法叫“is a”
当两个类满足语法什么是什么的时候,就可以使用继承了
Person
Teacher
Student
Doctor
液体
可乐
水
牛奶
package com.shujia.wxl.day10;
class Father5{
int num = 20;
private int num2 = 30;
Father5(){
System.out.println("这是父亲的无参构造方法");
}
private void fun1(){
System.out.println(num);
}
}
class Son5 extends Father5{
public void fun2(){
System.out.println(num);
// System.out.println(num2); // 子类不能继承父类的成员变量
}
}
public class ExtendsDemo4 {
public static void main(String[] args) {
Son5 son5 = new Son5();
son5.fun2();
// son5.fun1(); // 子类不能继承父类的私有成员方法
}
}
5、注意事项二
继承的使用注意事项2:
类的组成:
成员变量
构造方法
成员方法
引入了继承,我们需要注意在写代码的过程中考虑继承关系带来的影响
继承与成员变量之间的关系:
1、当子类中的成员变量与父类中的成员变量名字一样的时候
查找:(就近原则)
1)先在方法的局部范围内进行查找,如果找到就返回
2)如果在方法局部范围找不到,去本类中成员位置上查找,如果找到就返回
3)如果在中成员位置上找不到,去父类中成员位置上查找,如果找到就返回
4)如果在父类中成员位置上找不到,报错。
2、当子类中的成员变量与父类中的成员变量名字不一样的时候,使用什么变量名,就访问谁
class Father6{
int num = 10;
int num2 = 200;
public void show2(){
int num = 100;
int num2 = 20;
}
}
class Son6 extends Father6{
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 ExtendsDemo5 {
public static void main(String[] args) {
Son6 son6 = new Son6();
son6.show1();
}
}
6、继承中常见的问题
继承中出现的问题:
我们现在不仅仅要输出局部范围内的num,我们还想输出父类中num,怎么办呢?
如果有一个东西,根this相似,也可以代表父类的引用,通过这个东西去访问父类中的数据就好了。
Java替你考虑到了这个问题,提供了一个关键字给我们使用:super
面试题:this关键字与super关键字的使用区别?
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;//局部范围的num
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();
}
}
7、继承与构造方法的关系
(1)继承与构造方法的关系:
1、要想初始化子类,必选先初始化父类
2、为什么?
因为子类会继承父类的数据,甚至可能会使用父类的数据
所以在子类初始化之前,一定会先完成父类的初始化
注意:
每个子类的构造方法的第一句话默认是super().
class Father8 {
int age;
Father8() {
System.out.println("父类中的无参构造方法");
}
Father8(String s) {
System.out.println("这是父类带参数的构造方法" + s);
}
}
class Son8 extends Father8 {
Son8() {
System.out.println("子类的无参构造方法");
}
Son8(String s) {
System.out.println("子类带参数的构造方法" + s);
}
}
public class ExtendsDemo7 {
public static void main(String[] args) {
Son8 s1 = new Son8("王宇");
}
}
运行结果:
(2)当父类中没有无参构造方法的时候,怎么办呢?
1、使用super关键字带参数的形式访问父类的带参数构造方法
2、子类通过this关键字调用本类的其他构造方法
注意,本类其他构造方法也必须可以访问父类拥有的构造方法
使用this关键字间接的调用父类的构造方法
无论在哪里调用父类的构造方法,只要最后保证在子类构造方法内容执行之前完成了父类的初始化就可以了
3、super(..)或者this(..)必须出现在第一条语句上
2、否则就会出现父类的数据进行了多次初始化
(重点)每个类只能初始化一次
class Father9 {
int age;
// Father8() {
// System.out.println("父类中的无参构造方法");
// }
Father9(String s) {
System.out.println("这是父类带参数的构造方法" + s);
}
}
class Son9 extends Father9 {
Son9() {
super("你好");
System.out.println("子类的无参构造方法");
}
Son9(String s) {
this();
// super("你也好");
System.out.println("子类带参数的构造方法" + s);
// this();
}
}
public class ExtendsDemo8 {
public static void main(String[] args) {
Son9 s1 = new Son9("王宇");
}
}
8、继承与成员方法的关系
继承与成员方法的关系:
1、当子类的成员方法名与父类成员方法名不一样的时候,该调用谁就调用谁的
2、当子类的成员方法名与父类成员方法名一样的时候,怎么办呢?(就近原则)
1)现在本类中查找,如果有就调用,如果没有去父类中查找,
2)如果父类中有方法,就调用父类的
3)如果连父类中都没有要调用的方法名,报错,提示找不到方法。
class Father10{
public void show(){
System.out.println("这是父类中的show方法");
}
}
class Son10 extends Father10{
// public void show(){
// System.out.println("这是子类中的show方法");
// }
public void show2(){
System.out.println("这是子类中的show2方法");
}
}
public class ExtendsDemo9 {
public static void main(String[] args) {
Son10 son10 = new Son10();
son10.show2();
son10.show();
}
}
9、重写与重载
子类的方法名能不能和父类方法的声明一样?
如果子类的方法声明与父类的方法声明一样,这样的现象叫做方法的重写。
重写现象是发生在继承的关系中。
面试题:
重写与重载的区别:
重写是发生在继承的关系的关系中,重载是发生在本类中。
重载是方法名一致,参数列表不一致就是重载。
重写是方法名,参数列表,返回值都一样,实现不一样,叫方法的重写。
重载的英文单词:overload
重写的英文单词:override
生活中有哪些重写的案例?
水杯--保温杯
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 void call(String name){
// System.out.println("打电话给"+name);
// System.out.println("看抖音");
// }
}
public class ExtendsDemo10 {
public static void main(String[] args) {
newPhone newPhone = new newPhone();
newPhone.call("王宇");
}
}