Java语言基础第八讲
一.代码块的概述和分类
- 概述
- 在Java中使用{}括起来的代码被称为代码块.
- 代码块分类 :
- 根据位置和声明不同,分为局部代码块,构造代码块,静态代码块,同步代码块(多线程学).
常见代码块的应用
- 局部代码块—在方法中出现,限定生命周期,及早释放,提高内存利用率.
- 构造代码块—在类中方法外出现,多个构造方法中相同的代码存放在一起,每次调用构造都执行,并且优先于构造方法执行.
静态代码块—在类中方法外出现,并用static 修饰,用于给类进行初始化,在加载的时候就执行(即在加载主类的时候调用一次),并且优先于构造方法执行,一般用于加载驱动.
class Demo_Code { public static void main(String[] args) { { int x = 10; //局部代码块,定义在一对{}之内,用来限定代码生命周期 System.out.println(x); } Student s = new Student(); System.out.println("-----------------"); Student s1 = new Student("张撒",23); } static { //主方法的静态代码块优先于主方法执行 System.out.println("主方法的静态代码块"); } } class Student { private String name; private int age; public Student() { study(); System.out.println("空参构造"); } public Student(String name,int age) { this.name = name; this.age = age; study(); System.out.println("有参构造"); } 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 study() { System.out.println("学生学习"); } { //构造代码块,类中方法外 //每创建一次对象,就执行一次,优先于构造方法执行 System.out.println("构造代码块"); } static { //静态代码块,类中方法外,随着类的加载而加载,只执行一次 //用来给类初始化 System.out.println("主方法的静态代码块"); } }
面试题:看程序,写结果
class Test2_Student { public static void main(String[] args) { System.out.println("我是main方法"); Student s1 = new Student(); Student s2 = new Student(); } static { System.out.println("Demo2_Student静态代码块"); } } class Student { static { System.out.println("Student 静态代码块"); } { System.out.println("Student 构造代码块"); } public Student() { System.out.println("Student 构造方法"); } } /*执行结果: Demo2_Student静态代码块 //优先于主方法执行 我是main方法 Student 静态代码块 Student 构造代码块 Student 构造方法 Student 构造代码块 Student 构造方法 */
二.继承的概述
- 继承(extends)概念 :
- 让类与类之间产生关系,子父类关系
- 继承的好处和弊端:
- 好处—-
- 1.提高了代码的复用性.
- 2.提高了代码的可维护性.
- 3.让类与类之间产生关系,是多态的前提;
- 弊端—- 类的耦合性增强了
- 好处—-
- 开发的原则:
- 高内聚(内聚指的是自己完成一件事的能力);
- 低耦合(耦合指的是类与类的关系).
继承案例演示:
- 动物类,猫类,狗类
定义两个属性(颜色,腿的个数)两个功能(吃饭,睡觉)
class Demo1_Extends { public static void main(String[] args) { Cat c = new Cat(); c.color = "花"; c.leg = 4; c.eat(); c.sleep(); System.out.println(c.leg + "..." + c.color); } } class Animal { String color; //动物的颜色 int leg; //动物腿的个数 public void eat() { //吃饭的功能 System.out.println("吃饭"); } public void sleep() { //睡觉的功能 System.out.println("睡觉"); } } class Cat extends Animal { } class Dog extends Animal { } /* extends是继承的意思 Animal是父类 Cat和Dog都是子类 */
Java中类的继承特点
- java中只支持单继承,不支持多继承;(一个儿子只能有一个人亲爹).
java中支持多层继承(继承体系);
- 如果想用这个体系的所有功能,用底层的类创建对象;
如果想用这个体系的共性功能,看最顶层的类.
//多层继承,B继承A,C继承B. class Demo2_Extends { public static void main(String[] args) { DemoC d = new DemoC(); d.show(); } } class DemoA { public void show() { System.out.println("DemoA"); } } class DemoB extends DemoA { public void method() { System.out.println("DemoB"); } } class DemoC extends DemoB { public void print() { System.out.println("DemoC"); } }
- 继承的注意事项:
- 子类只能继承父类所有非私有的成员(成员变量和成员方法);
- 子类不能继承父类的构造方法,但是可以通过 super 关键字来访问父类构造方法;
- 不能为了部分功能去继承.
- 什么时候使用继承?
- 继承其实体现的是一种关系” is a “.
继承中成员变量的关系
- 同名变量
不同名变量
//同名和不同名变量 class Demo4_Extends { public static void main(String[] args) { Son s = new Son(); s.print(); } } class Father { int num1 = 10; int num2 = 30; //先演示的时候,不加此处的num2 } class Son extends Father { int num2 = 20; public void print(){ System.out.println(num1); System.out.println(num2); //num = 20,就近原则 } }
三.this 和 super 的区别以及应用场景
- this 和 super 分别代表什么.
- this 代表的是当前对象的引用,谁来调用我,我就代表谁;
- super 代表当前对象父类的引用.
this和super的区别:
- 调用成员变量.
- this.成员变量,调用本类中成员变量,也可以调用父类的成员变量
- super.成员变量,调用父类的成员变量.
- 调用成员方法
- this.成员方法,调用本类的成员方法,也可以调用父类的成员方法.
- super.成员方法,调用父类的成员方法.
调用构造方法
- this(…) 调用本类的构造方法;
super(…) 调用父类的构造方法.
class Demo4_Extends { public static void main(String[] args) { Son s = new Son(); s.print(); } } class Father { int num1 = 10; int num2 = 30; } class Son extends Father { int num2 = 20; public void print() { System.out.println(this.num1); //this既可以调用本类的,也可以调用父类的(本类没有的情况下) System.out.println(this.num2); //就近原则,子类有就不用父类的了 System.out.println(super.num2); } }
- 调用成员变量.
继承中构造方法的关系
- 子类中所有构造方法默认都会访问父类中的空参构造方法.
- 原因—-因为子类会继承父类的数据,可能还会使用父类的数据,所以子类在初始化之前,一定要先完成父类数据的初始化.
其实,每一个构造方法的第一条语句默认都是super() object 类最顶层的父类.
class Demo5_Extends { public static void main(String[] args) { Son s = new Son(); } } class Father extends Object { public Father() { //super(); System.out.println("Father 的构造方法"); } } class Son extends Father { public Son() { //super(); //这是一条语句,如果不写,系统会默认加上,用来访问父类中的空参构造 System.out.println("Son 的构造方法"); } }
- 子类中所有构造方法默认都会访问父类中的空参构造方法.
四.继承中构造方法的注意事项
- 父类没有无参构造方法,子类怎么办?
- super解决:用super(….)显式调用父类的有参构造,如:super(name,age);
- this解决:通过this调用本类的有参构造,然后在本类的有参构造里,又调用了父类的有参构造,相当于间接调用父类的有参构造.
注意事项
- super(…)或者this(….)必须出现在构造方法的第一条语句上,只能出现其一
super和this两个关键字可以共存,但this和super语句不能共存.
class Demo6_Extends { public static void main(String[] args) { Son s1 = new Son(); System.out.println(s1.getName() + "..." + s1.getAge()); System.out.println("--------------------"); Son s2 = new Son("张三",23); System.out.println(s2.getName() + "..." + s2.getAge()); } } class Father { private String name; //姓名 private int age; //年龄 public Father() { //空参构造 System.out.println("Father 空参构造"); } public Father(String name,int age) { //有参构造 this.name = name; this.age = age; System.out.println("Father 有参构造"); } 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 Son extends Father { public Son() { //空参构造 this("王五",25); //本类中的构造方法 //super("李四",24); //调用父类中的构造方法 System.out.println("Son 空参构造"); } public Son(String name,int age) { //有参构造 super(name,age); System.out.println("Son 有参构造"); } }
继承中的面试题
//案例一 class Fu{ public int num = 10; public Fu(){ System.out.println("fu"); } } class Zi extends Fu{ public int num = 20; public Zi(){ //super(); System.out.println("zi"); } public void show(){ int num = 30; System.out.println(num); System.out.println(this.num); System.out.println(super.num); } } class Test1_Extends { public static void main(String[] args) { Zi z = new Zi(); z.show(); } } //案例二: 1,jvm调用了main方法,main进栈,因为子要使用父类的东西,所以父类要先加载 2,遇到Zi z = new Zi();会先将Fu.class和Zi.class分别加载进内存,再创建对象,当Fu.class加载进内存 父类的静态代码块会随着Fu.class一起加载,当Zi.class加载进内存,子类的静态代码块会随着Zi.class一起加载 第一个输出,静态代码块Fu,第二个输出静态代码块Zi 3,走Zi类的构造方法,因为java中是分层初始化的,先初始化父类,再初始化子类,所以先走的父类构造,但是在执行 父类构造时,发现父类有构造代码块,构造代码块是优先于构造方法执行的所以 第三个输出构造代码块Fu,第四个输出构造方法Fu 4,Fu类初始化结束,子类初始化,第五个输出的是构造代码块Zi,构造方法Zi */ class Test2_Extends { public static void main(String[] args) { Zi z = new Zi(); } /* } class Fu { static { System.out.println("静态代码块Fu"); } { System.out.println("构造代码块Fu"); } public Fu() { System.out.println("构造方法Fu"); } } class Zi extends Fu { static { System.out.println("静态代码块Zi"); } { System.out.println("构造代码块Zi"); } public Zi() { System.out.println("构造方法Zi"); } }
五.继承中成员方法的关系
- 不同名的方法:直接调用就可以
同名的方法:称为方法的重写,用子类对象直接调用的是子类已经重写的方法
要想调用父类的方法,可以在子类重写的方法中添加super();
class Demo7_Extends { public static void main(String[] args) { Son s = new Son(); s.print(); s.method(); } } class Father { public void print() { System.out.println("Fu print"); } } class Son extends Father { public void method() { System.out.println("Zi Method"); } public void print() { super.print(); //super可以调用父类的成员方法 System.out.println("Zi print"); } }
六.方法重写(override)的概述与应用
- 什么是方法重写
- 重写指的是子父类出现了一模一样的方法.(返回值类型可以子父类,这个得要学完面向对象中讲)
- 方法重写的应用
- 当子类需要父类的功能,而功能主体子类有自己特有的内容时,可以重写父类中的方法,这样既延续了父类的功能,有定义了子类特有的内容.
- 方法重写的注意事项:
- 父类中私有方法不能被重写(因为父类私有方法子类根本就无法继承)
- 子类重写父类方法时,子类方法的访问权限不能小于父类的访问权限;
- 静态只能覆盖静态,不能覆盖非静态,非静态也不能覆盖静态.
- 重写与重载的区别:
- 重写(override)指的是子父类出现了一么一样的方法,与返回值类型有关,自子父类返回值类型是一致的.
- 重载(overland)指的是在同一个类中,方法名相同,参数列表不同,与返回值类型无关.
- 子类对父类调用方法的时候:
- 先找子类本身,再找父类.
使用继承后的学生和老师的案例
- 属性:姓名,年龄;
- 行为:吃饭.
- 老师特有的方法:讲课
学生特有的方法:学习
class Demo_Person { public static void main(String[] args) { Student s = new Student("小明",12); System.out.println("姓名是:" + s.getName() + ",年龄是:" + s.getAge()); s.study(); } } class Person { private String name; private int age; public Person() {} public Person(String name,int age) { this.name = name; this.age = 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; } public void eat() { System.out.println("吃饭"); } } class Teacher extends Person { public Teacher() {} public Teacher(String name,int age) { super(name,age); } public void teach() { System.out.println("老师讲课"); } } class Student extends Person { public Student() {} public Student(String name,int age) { super(name,age); } public void study() { System.out.println("学生学习"); } }
猫狗类型案例.
- 猫狗案例继承版
- 属性:毛的颜色,腿的个数
- 行为:吃饭
- 猫特有行为:抓老鼠catchMouse
狗特有行为:看家lookHome
class Demo_Animal { public static void main(String[] args) { Cat c = new Cat("黄",4); System.out.println(c.getColor() + "---" + c.getLeg()); c.eat(); c.catchMouse(); System.out.println("-----------"); Dog d = new Dog("黑",4); System.out.println(d.getColor() + "---" + d.getLeg()); d.eat(); d.lookHome(); } } class Animal { private String color; private int leg; public Animal() {} public Animal(String color,int leg) { this.color = color; this.leg = leg; } public void setColor(String color) { this.color = color; } public String getColor() { return color; } public void setLeg(int leg) { this.leg = leg; } public int getLeg() { return leg; } public void eat() { System.out.println("吃饭"); } } class Dog extends Animal { public Dog() {} public Dog(String color,int leg) { super(color,leg); } public void eat() { System.out.println("狗吃肉"); } public void lookHome() { System.out.println("看家"); } } class Cat extends Animal { public Cat() {} public Cat(String color,int leg) { super(color,leg); } public void eat() { System.out.println("猫吃鱼"); } public void catchMouse() { System.out.println("抓老鼠"); } }
七.final关键字概述及其特点.
- final 概述: 用于修饰类变量,变量,方法,是”最终”的意思.
- final的特点
- final修饰的类不能被继承
- final修饰的方法不能被子类重写
- final修饰的变量都是常量,只能赋值一次.
常量命名规范:如果是一个单词,所有字母大写,如果是多个单词,每个单词都大写,中间用下划线隔开
class Demo1_Final { public static void main(String[] args) { Son s = new Son(); s.print(); } } final class Father { //final修饰的类不能被继承 public void print() { System.out.println("访问底层数据资源"); } } class Son extends Father { final int NUM = 10; public static final double P1 = 3.14; //final修饰变量叫做常量,一般会与public static共用 public void print() { NUM = 20; System.out.println(NUM); } }
final修饰局部变量:
- 基本数据类型,值不能改变
引用数据类型,地址不能改变,但是地址所指向的对象的属性可以改变
class Demo2_Final { public static void main(String[] args) { final int num = 10; //基本数据类型,值不能改变 num = 20; System.out.println(num); final Person p = new Person("张三",23); p = new Person("李四",24); //引用数据类型,地址不能改变,但是地址所指向的对象的属性可以改变 p.setName("李四"); p.setAge(24); System.out.println(p.getName() + "..." + p.getAge()); method(10); method(20); } public static void method(final int x) { System.out.println(x); } }
final修饰变量的初始化时机
- 显示初始化
在对象构造完毕前即可
class Demo3_Final { public static void main(String[] args) { Demo d = new Demo(); d.print(); } } class Demo { //final int num = 10; //表示显式初始化 final int num; //成员变量的默认初始化值是无效值,因为final修饰的变量只能被赋值一次, //所以会特别珍惜这次赋值,所以系统的默认初始化值是无效的 public Demo() { num = 10; } public void print() { System.out.println(num); } }