目录
一、代码块
在java中被大括号{}括起来的代码被称为代码块。
根据位置和声明的不同,一般代码块可以分为:
- 局部代码块:在方法中出现;限定变量生命周期,及早释放,提高内存利用率
- 构造代码块:在类中方法外出现; 多个构造方法中相同的代码存放到一起,每次调用构造都执行该代码块,并且在构造方法前执行
- 静态代码块:在类中方法外出现,加上static修饰,随着类的加载而加载,且只执行一次,用于类的初始化,一般用来加载驱动(静态代码块优先于主方法执行)
- 在类中方法外出现,加上static修饰,随着
- 在类中方法外出现,
- 同步代码块(多线程)
下面我们通过代码来体会这几个代码块执行的顺序:
public class Main {
public static void main(String[] args){
{
System.out.println("主函数");
}
Person p1=new Person();
Person p2=new Person();
}
static{
System.out.println("静态代码块");
}
}
class Person{
{
System.out.println("构造代码块");
}
public Person(){
System.out.println("构造方法");
}
}
下面是运行的结果:
通过输出的顺序可以看出执行顺序为:
静态代码块->主函数->构造代码块->构造方法
并且静态代码块只执行一次,随着类的加载而加载,
构造代码块和构造方法随着对象的实例化加载而加载,并且构造代码块在构造方法之前运行。
二、继承
2.1继承
继承即在已有的类的基础上创建新的类,通过复用基础类的一些方法并且增加新的方法和字段,来增加重复代码的利用率。
这里我们使用一个简单的例子说明java如何使用继承关系:
public class Main {
public static void main(String[] args){
Employee e1=new Employee();
System.out.println(e1.getSalary());
Manager m1=new Manager();
System.out.println(m1.getSalary());
}
}
class Employee{
private double salary;
public Employee(){
this.salary=5000;
}
public double getSalary(){
return this.salary;
}
}
class Manager extends Employee{
private double Bonus;
public Manager(){
this.Bonus=5000;
}
public double getSalary(){
return (this.Bonus+super.getSalary());
}
}
由上述程序可知,我们定义了一个基类Employee,雇员有自己的初始工资5k,方法getSalary获得雇员的工资,
然后我们通过extends关键字继承了基类Employee定义了心累Manager,经理在初始工资的基础上多了一个奖金,奖金初始化为5k,
然后经理通过对方法getSalary的重写,获得经理的工资为初始工资+奖金。
最后输出结果如下:
可以看到通过继承雇员的类,然后对雇员的类进行拓展生成了新的类manager,
这样如果公司的初始工资发生变化,就不用对所有类的初始工资都进行更改,
只用更改基类的工资就可以了,这种方式大大的提高了代码的复用性。
需要注意的是,如果在子类中定义了与父类中已定义的同名变量,
那么在子类中如果对该变量进行访问,则以子类中的变量值为准,父类中的变量相当于被“覆盖”了。
2.2继承的优缺点与特点
继承的好处:
- 提高了代码的复用性
- 提高了代码的维护性
- 让类与类之间产生了关系,是多态的前提
继承的弊端:
- 类的耦合性增强了
在开发过程中,我们往往要求程序具有高内聚、低耦合的特点,
即类与类之间的相关性减少,让每个类之间尽量相互独立,这样就可以用尽可能少的类完成更多的事。
继承的特点:
- Java中支持单继承,不支持多继承(C++中就支持多继承),即一个类只允许继承一个基类,不能继承多个基类
- Java支持多层次继承,比如B继承于A,而C又继承于B,这样A就是最基本的类
三、this与super的区别于应用
this代表对象自身,可以理解为对象本身的一个引用,
super则代表父类对象,可以理解为父类对象的引用。
在使用中this既可以调用自身的变量或者方法,在自身没找到变量或方法名时,this会查找父类的变量或者方法进行调用,
而super只能调用父类的变量和方法。
四、继承中的构造方法
子类中所有的构造方法都默认会访问父类中无参构造方法,
因为子类一般会继承父类的变量,在子类中对这些变量进行访问,
所以当子类进行初始化时,会先执行父类中无参构造方法对父类的变量进行初始化。
public class Main {
public static void main(String[] args){
Son son =new Son();
}
}
class Father{
public Father(){
System.out.println("父类构造方法");
}
}
class Son extends Father{
public Son(){
System.out.println("子类构造方法");
}
}
上述代码执行结果如下图:
可以看到在主函数中实例化Son对象后,先执行了父类的构造方法,然后执行了子类的构造方法。
这是因为在子类的构造函数中,系统会自动帮我们补上一句:
super();
其作用是访问父类的无参构造方法。
其实每一个构造方法的第一条语句默认都是super(),所有类都是继承于Object类。
既然要访问父类中的无参构造方法,
如果父类中没有无参构造方法,那么子类中必须显示调用父类的有参构造方法初始化变量:
子类构造方法(参数列表){
super(参数列表);
...
}
五、方法的重写
5.1方法重写
方法的重写是当父类的方法在子类中重新实现,方法名称、参数列表、返回值类型均相同。
当子类需要父类的功能,而当前功能子类又有自己的实现特点时,那么通过方法的重写可以复用父类的其他功能,也定义了自己的内容。
比如在之前雇员与经理的例子中获取工资的方法,
雇员的获取工资方法为输出初始工资变量,而经理获取工资的方法为输出初始工资加奖金。
class Employee{
public double getSalary(){
return this.salary;
}
}
class Manager extends Employee{
public double getSalary(){
return (this.Bonus+super.getSalary());
}
}
这种就是方法的重写,如果还想继续使用父类中被覆盖的方法,那么使用super.方法名()也可以调用父类的方法。
5.2重写注意事项
在进行方法的重写时,有以下几点注意事项:
- 父类中私有的方法无法被重写(因为私有方法无法被继承也就没办法被重写)
- 子类在重写父类方法时,访问的权限不能越来越低,比如父类的方法为public时,子类重写时不能将权限改为比public低的protected或者private
- 父类中静态的方法,子类在进行重写时也必须为静态方法
- 子类重写父类方法时,最好声明一样
5.3方法重写和方法重载的区别
- 方法重写:子类中出现了与父类中方法声明一模一样的方法,与返回值类型有关,返回值是一致的。
- 方法重载:本类中出现的方法名一样,参数列表不同的方法,与返回值类型无关。
在子类对象调用方法时,其一般顺序为现在子类本身的方法中寻找,找不到则取父类继承的方法中找。
六、final关键字
final作为一个关键字,可以用来修饰类、变量和方法:
- final修饰一个类时,该类不能被继承
- final修饰一个变量时,变量被赋值后它的值就无法被修改
- 当变量为基本数据类型时,其值无法被改变
- 当变量为引用数据类型时,其地址值无法改变,但是对象中的属性可以改变
- final修饰方法时,方法无法被重写
final修饰变量时必须要显示初始化,给一个初始值,不能不写用java默认值,
如果不给一个显示的初始化值,则必须在构造方法内对final的变量进行初始化。