在Java中,使用{}括起来的代码被称为代码块。
代码块的分类,根据其位置和声明的不同,可以分为局部代码块,构造代码块,静态代码块,同步代码块。
局部代码块:在方法中出现;限定变量生命周期,及早释放,提高内存利用率。
构造代码块:在类中方法外出现;多个构造方法方法中相同的代码存放到一起,每次调用构造都执行,并且在构造方法前执行。
静态代码块:在类中方法外出现,加上了static修饰;用于给类进行初始化,在加载的时候就执行,并且只执行一次。
A:看程序写结果
class Student {
static {
System.out.println("Student 静态代码块");
}
{
System.out.println("Student 构造代码块");
} //构造代码块,优先于构造方法执行
//每创建一次,构造代码块都要执行
public Student() {
System.out.println("Student 构造方法");
}
}
class StudentDemo {
static {
System.out.println("StudentDemo的静态代码块");
}
public static void main(String[] args) {
System.out.println("我是main方法");
Student s1 = new Student();
Student s2 = new Student();
}
}
输出:
StudentDemo的静态代码块
我是main方法
Student 静态代码块
Student 构造代码块
Student 构造方法
Student 构造代码块
Student 构造方法
分析:先找main()方法,在StudentDemo类中,按顺序朝下走:
1、先执行StudentDemo类中的静态代码块(只执行一次)——StudentDemo的静态代码块
2、再执行main()方法——我是main方法
3、创建s1对象,调用StudentL类,按顺序执行,先执行Student类中的静态代码块——Student 静态代码块,构造代码块优先于构造方法执行,——Student 构造代码块,最后执行构造方法——Student 构造方法
4、创建s2对象,同样调用Student类,不再执行静态代码块,构造代码块和构造方法依次执行——Student 构造代码块——Student 构造方法
输出:
StudentDemo的静态代码块
我是main方法
Student 静态代码块
Student 构造代码块
Student 构造方法
Student 构造代码块
Student 构造方法
多个类中存在相同属性和行为时,将这些内容抽取到单独的一个类中,那么多个类无需再定义这些属性和行为,只要继承那个类即可。
通过extends关键字可以实现类与类的继承。
class 子类名 extends 父类名 {}
单独的这个类称为父类,基类或者超类,这多个类可以称为子类或者派生类。
public class Test {
public static void main(String[] args) {
Student student = new Student();
// Teacher student = new Student(); 也可以创建学生对象
student.teaEat();
student.teaSleep();
}
}
class Teacher {
public void teaEat() {
System.out.println("我要吃饭了");
}
public void teaSleep() {
System.out.println("我要睡觉了");
}
}
class Student extends Teacher {
}
这是一个简单的继承,学生类继承了老师类的方法,学会了吃饭和睡觉。
继承的好处
1、提高了代码的复用性。
2、提高了代码的维护性。
3、让类与类之间产生了关系,是多态的前提。
继承的弊端
类的耦合性增强了。
开发的原则是:高内聚,低耦合
耦合:类与类的关系
内聚:就是自己完成某件事情的能力
1、Java中只支持单继承,不支持多继承。有些语言是支持多继承的。
2、java支持多层继承(继承体系)
案例:创建一个动物类,实现两个方法:吃饭和睡觉,再分别创建狗类和猫类来继承动物类,再分别实现各自独有的方法;
public class Test {
public static void main(String[] args) {
Dog dog = new Dog();
dog.name = "大黄";
dog.age = "2";
dog.Eat();
dog.Sleep();
dog.action();
Cat cat = new Cat();
cat.name = "罗小黑";
cat.age = "1";
cat.Eat();
cat.Sleep();
cat.action();
}
}
class Animal {
int age;
String name;
public void Eat() {
System.out.println("吃饭饭");
}
public void Sleep() {
System.out.println("睡觉觉");
}
}
class Dog extends Animal {
public void action() {
System.out.println("我能看门");
}
}
class Cat extends Animal {
public void action() {
System.out.println("我能抓老鼠");
}
}
1、子类只能继承父类所有非私有的成员(成员方法和成员变量)
2、子类不能继承父类的构造方法,但可以通过super关键字去访问父类构造方法
3、不要为了部分功能去继承。
父类的构造方法的作用其实是让子类完成对父类数据的初始化,子类要继承父类数据,有可能还要用父类的数据,如果父类没有先完成初始化,那么父类的数据子类就没法用。
继承其实体现的使用“is a”的关系。如果有两个类型A,B。只有他们符合A是B的一种,或者B是A的一种,就可以考虑使用继承。
在子类中访问一个变量的查找顺序“就近原则”:
a、在子类的方法的局部范围找,有就调用
b、在子类的成员范围找,有就调用
c、在父类的成员范围中找,有就调用
d、如果还找不到,就报错
public class Test {
public static void main(String[] args) {
Son son = new Son();
son.show(20);
}
}
class Father {
int age = 100;
}
class Son extends Father {
int age = 50;
public void show(int age) {
System.out.println(age); //20
System.out.println(this.age); //50
System.out.println(super.age); //100
}
}
子类局部范围访问父类成员变量。
this : 代表的是本类对象的引用
super : 代表的是父类存储空间的标识(可以理解成父类的引用,可以操作父类的成员)
this 代表子类的一个引用,你可以理解为子类的一个对象,谁调用我,我就代表谁
super是父类空间的一个标识,你可以理解为父类的一个对象,既然super 是父类空间的一个标识,那么就可以通过super去访问父类的数据.
a:调用成员变量
this.成员变量 —— 调用本类的成员变量
super.成员变量 ——调用父类的成员变量
b:调用构造方法
this(…) ——调用本类的构造方法
super(…) ——调用父类的构造方法
c:调用成员方法
this.成员方法 ——调用本类的成员方法
super.成员方法 ——调用父类的成员方法
public class Test {
public static void main(String[] args) {
Son son = new Son();
son.show(20);
}
}
class Father {
int age = 100;
}
class Son extends Father {
int age = 50;
public void show(int age) {
System.out.println(age); //20
System.out.println(this.age); //50
System.out.println(super.age); //100
}
}
子类中所有的构造方法默认都会访问父类中空参数的构造方法
因为子类会继承父类中的数据,可能还会使用父类的数据。
所以,子类初始化之前,一定要先完成父类数据的初始化。
其实,每一个构造方法的第一条语句默认都是:super();
类 Object 是类层次结构的根类。每个类都使用 Object 作为超类,
Object类 是我们所有类的顶层父类,所有类都是直接或简介继承自他。
class Fu{
public int num = 10;
public Fu(){
System.out.println("fu");
}
}
class Zi extends Fu{
public int num = 20;
public Zi(){
System.out.println("zi");
}
public void show(){
int num = 30;
System.out.println(num); //30
System.out.println(this.num); //20
System.out.println(super.num); //10
}
}
class Test {
public static void main(String[] args) {
Zi z = new Zi();
z.show();
}
}
输出:
fu
zi
30
20
10
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");
}
}
class Test {
public static void main(String[] args) {
Zi z = new Zi();
}
}
输出:
静态代码块Fu
静态代码块Zi
构造代码块Fu
构造方法Fu
构造代码块Zi
构造方法Zi
通过子类调用方法时,先查找子类中有没有该方法,如果有就使用;子类中没有再看父类中有没有该方法,有就使用。
子类中出现了和父类中一模一样的方法声明(方法名,参数列表,返回值类型),也被称为方法覆盖,方法复写。
当父类中的方法不能满足子类的需要时,子类就可以重写父类中的方法,这样既保留了父类中的功能,又有了新的功能。
Override:方法重写
Overload:方法重载
重载Overload表示同一个类中可以有多个名称相同的方法,但这些方法的参数列表各不相同(即参数个数或类型不同)。
重写Override表示子类中的方法可以与父类中的某个方法的名称和参数完全相同,通过子类创建的实例对象调用这个方法时,将调用子类中的定义方法,这相当于把父类中定义的那个完全相同的方法给覆盖了,这也是面向对象编程的多态性的一种表现。
1、父类中的私有方法不能被重写,因为父类私有方法子类根本无法继承。
2、子类重写父类方法时,访问权限不能更低。
3、父类静态方法,子类也必须通过静态方法重写(实际上这算不上方法重写)
修饰类:被修饰的类不能被继承
修饰方法:被修饰的方法不能被重写
修饰变量:被修饰的变量不能被重新赋值,这位这个量其实是个常量
基本类型,是值不能被改变
引用类型,是地址值不能被改变