目录
代码块
在Java中,使用{}括起来的代码被称为代码块,根据其位置和声明的不同,可以分为局部代码块,构造代码块,静态代码块,同步代码块(到多线程再说)。
a.局部代码块:
在方法中出现;限定变量生命周期,及早释放,提高内存利用率。
在同一个类的同一个方法中,如果存在多个局部代码块,执行顺序是自上而下的
public class Test1 {
public static void main(String[] args) {
//第一个代码块
{
int a=100;
System.out.println(a);
}
//第二个代码块
{
int b =200;
System.out.println(b);
}
//第三个代码块
{
int c=300;
System.out.println(c);
}
}
}
b.构造代码块:
在类中方法外出现;多个构造方法方法中相同的代码存放到一起,每次调用构造都执行,并且在构造方法前执行。
class Demo{
//创建一个无参构造方法
Demo(){
int a=10;
System.out.println(a);
}
//创建一个构造代码块(定义在类中方法外,用大括号括起来的代码,称为构造代码块)
{
int b=20;
System.out.println(b);
}
}
public class Test2 {
public static void main(String[] args) {
//创建Demo的对象
Demo demo = new Demo();
//创建一个局部代码块
{
int c=30;
System.out.println(c);
}
}
}
可以看出当一个类中既存在构造方法又存在构造代码块,在创建对象时,会先执行构造代码块,再执行构造方法,而main方法里还是自上而下执行。
c.静态代码块
在类中方法外出现,并加上static修饰;用于给类进行初始化,在加载的时候就执行,并且值执行一次。
class Demo{
//创建一个无参构造方法
Demo(){
int a=10;
System.out.println(a);
}
//创建一个构造代码块(定义在类中方法外,用大括号括起来的代码,称为构造代码块)
{
int b=20;
System.out.println(b);
}
//创建一个静态代码块
static {
int d=40;
System.out.println(d);
}
}
public class Test2 {
public static void main(String[] args) {
//创建Demo的对象
Demo demo = new Demo();
//创建一个局部代码块
{
int c=30;
System.out.println(c);
}
}
}
静态代码块优先其他代码块执行,不创建对象也会执行。
最后我们整合一下:
class Demo3{
Demo3(){
System.out.println("Demo3的无参构造方法");
}
static {
System.out.println("Demo3中的静态代码块");
}
{
System.out.println("Demo3中的构造代码块");
}
}
public class Test3 {
static {
System.out.println("Test3中的静态代码块");
}
public static void main(String[] args) {
{
System.out.println("main方法中的局部代码块");
}
Demo3 demo3 = new Demo3(); //创建Demo3的对象
}
}
Demo3类需要创建对象才能访问,所以先走Test3类,Test3中静态代码块优先,再执行main方法里面的。直到创建了Demo3的对象,开始按静态代码块——构造代码块——构造方法的顺序执行。
继承
继承概述
a.多个类中存在相同属性和行为时,将这些内容抽取到单独一个类中,那么多个类无需再定义这些属性和行为,只要继承那个类即可。
b.通过extends关键字可以实现类与类的继承
格式: class 子类名 extends 父类名 {}
c.单独的这个类称为父类,基类或者超类;这多个类可以称为子类或者派生类。 有了继承以后,我们定义一个类的时候,可以在一个已经存在的类的基础上,还可以定义自己的新成员。
继承案列
class Father{
//创建父类,定义成员变量
String name;
int age;
public void sleep(){ //定义一个方法
System.out.println("睡觉");
}
}
class Son{
//创建一个子类,定义成员变量
String name;
int age;
public void sleep(){
System.out.println("睡觉");
}
}
当我们创建了两个或更多有共同之处的类的时候,发现这样就很麻烦,如上父与子类都有相同的成员变量,都有一个共同的方法,所以我们能利用继承来改进。
改进后:
class Person{ //定义一个人类,提取出父与子的相同处
String name;
int age;
public void sleep(){
System.out.println("睡觉");
}
}
class Father extends Person{ //创建父类继承人类
}
class Son extends Father{ //创建子类继承父类
public void play(){ //创建一个子类特有的方法
System.out.println("玩游戏");
}
}
public class Extends1 {
public static void main(String[] args) {
//创建父类对象
Father father = new Father();
father.sleep(); //父类通过继承能访问Person类的方法
//创建子类对象
Son son = new Son();
son.sleep(); //子类通过继承能访问Person类的方法
son.play(); //子类也可以访问自身特有的方法
}
}
使用继承后,父与子类都能访问Person类的方法,而且子类也能访问自己特有的方法。
继承的好处
a.提高了代码的复用性
多个类相同的成员可以放到同一个类中
b.提高了代码的维护性
如果功能的代码需要修改,修改一处即可
c.让类与类之间产生了关系,是多态的前提
其实这也是继承的一个弊端:类的耦合性很强
Java中继承的特点
a.Java只支持单继承,不支持多继承。
一个类只能有一个父类,不可以有多个父类。
class SubDemo extends Demo{} (正确)
class SubDemo extends Demo1,Demo2... (错误)
b.Java支持多层继承(继承体系)
class A{}
class B extends A{}
class C extends B{}
Java中继承的注意事项
a.子类只能继承父类所有非私有的成员(成员方法和成员变量)
其实这也体现了继承的另一个弊端:打破了封装性
b.子类不能继承父类的构造方法,但是可以通过super关键字去访问父类构造方法。
c.想要初始化子类,必须先初始化父类
d.不要为了部分功能而去继承
继承中成员变量的关系
案例:
class Father2 { //创建父类
int a = 40;
int b =50;
public void num2() {
System.out.println(a);
}
}
class Son2 extends Father2 { //子类继承父类
int a = 20;
public void num1() {
System.out.println(a);
}
public void num3(){
System.out.println(b);
}
}
public class Extends2 {
public static void main(String[] args) {
//创建子类对象
Son2 son2 = new Son2();
son2.num1(); //就近原则,输出的应该是Son2中的a
son2.num2(); //子类中没有该方法,到父类中寻找
son2.num3(); //子类中没有元素b,去父类寻找
}
}
结论:
在子类方法中访问一个变量
a.首先在子类局部范围找
b.然后在子类成员范围找
b.最后在父类成员范围找(肯定不能访问到父类局部范围)
d.如果还是没有就报错。(不考虑父亲的父亲…)
super关键字
class Father3{
int num=30;
}
class Son3 extends Father3{
int num =40;
public void show(){
int num=50;
System.out.println(num); //可以访问到show方法里面的num
System.out.println(this.num); //使用在封装里说的this关键字可以访问到类中的num
}
}
public class Extends3 {
public static void main(String[] args) {
Son3 son3 = new Son3();
son3.show();
}
}
如上,我们可以访问到子类方法里面的num,也可以利用this关键字访问子类中方法外的num,那如果我们想访问父类中的num呢,Java里提供了一个关键字:super
(在上面代码子类show方法里加入 System.out.println(super.num);就可以访问父类里面的num)
super的用法和this区别
this代表本类对应的引用。
super代表父类存储空间的标识(可以理解为父类引用)
用法(this和super均可如下使用)
a.访问成员变量:
this.成员变量
super.成员变量
b.访问构造方法
this(…)
super(…)
c.访问成员方法
this.成员方法()
super.成员方法()
继承中构造方法的关系
子类中所有的构造方法默认都会访问父类中空参数的构造方法。
因为子类会继承父类中的数据,可能还会使用父类的数据。所以,子类初始化之前,一定要先完成父类数据的初始化。 每一个构造方法的第一条语句默认都是:super()
例:
class Father4{
Father4(){
System.out.println("这是父类中的无参构造方法");
}
Father4(String s){
System.out.println("这是父类中的有参构造方法"+s);
}
}
class Son4 extends Father4{
Son4(){
System.out.println("这是子类中的无参构造方法");
}
Son4(String s){
System.out.println("这是子类的有参构造方法"+s);
}
}
public class Extends4 {
public static void main(String[] args) {
//创建子类对象
Son4 son4 = new Son4("User");
}
}
当父类中没有无参构造方法:
a.子类通过super去显示调用父类其他的带参的构造方法
例:
class Father4{
Father4(String s){
System.out.println("这是父类中的有参构造方法"+s);
}
}
class Son4 extends Father4{
Son4(){
super("FU");
System.out.println("这是子类中的无参构造方法");
}
Son4(String s){
super("FU");
System.out.println("这是子类的有参构造方法"+s);
}
}
public class Extends4 {
public static void main(String[] args) {
//创建子类对象
Son4 son4 = new Son4("User");
}
}
b.子类通过this去调用本类的其他构造方法
本类其他构造也必须首先访问了父类构造
例:
class Father4{
Father4(String s){
System.out.println("这是父类中的有参构造方法"+s);
}
}
class Son4 extends Father4{
Son4(){
super("FU");
System.out.println("这是子类中的无参构造方法");
}
Son4(String s){
this(); //使用this关键字,先调用子类的无参构造方法 ,间接的调用父类构造方法
System.out.println("这是子类的有参构造方法"+s);
}
}
public class Extends4 {
public static void main(String[] args) {
//创建子类对象
Son4 son4 = new Son4("User");
}
}
c.一定要注意: super(…)或者this(….)必须出现在第一条语句上, 否则,就会有父类数据的多次初始化
继承中成员方法的关系
子父类中同名和不同名的成员方法
class Father5{
public void show(){
System.out.println("父类中的show方法");
}
public void show3(){
System.out.println("父类中的show3方法");
}
}
class Son5 extends Father5{
public void show(){
System.out.println("子类中的show方法");
}
public void show2(){
System.out.println("子类中的show2方法");
}
}
public class Extends5 {
public static void main(String[] args) {
Son5 son5 = new Son5();
son5.show(); //调用子类的show方法,就近原则
son5.show2(); //调用子类的show2方法
son5.show3(); //子类访问show3方法,子类没有去父类中找
}
}
结论: 通过子类对象去访问一个方法,首先在子类中找,然后在父类中找,如果还是没有就报错。
方法重写概述
子类中出现了和父类中一模一样的方法声明,也被称为方法覆盖,方法复写。
使用特点:
如果方法名不同,就调用对应的方法
如果方法名相同,最终使用的是子类自己的
方法重写的应用:
当子类需要父类的功能,而功能主体子类有自己特有内容时,可以重写父类中的方法,这样,即沿袭了父类的功能,又定义了子类特有的内容。
例:
class Father6{
public void study(String s){
System.out.println("学习"+s);
}
}
class Son6 extends Father6{
public void study(String s){ //重写父类中的study方法
System.out.println("学习"+s+"中的继承");
}
public void study2(String s){
System.out.println("学习"+s); //子类特有的方法
}
}
public class Extends6 {
public static void main(String[] args) {
//创建子类对象
Son6 son6 = new Son6();
son6.study("java"); //调用子类的study方法
son6.study2("大数据");
}
}
方法重写的注意事项
a.父类中私有方法不能被重写
b.子类重写父类方法时,访问权限不能更低
c.父类静态方法,子类不能进行重写
方法重写与方法重载的区别
方法重写(override)是发生在继承关系中子类里的,方法重载(overload)是发生在同一个类中的
重载是方法名一样,参数列表不一样
重写是方法名,参数列表,返回值都一样,实现不一样,叫方法的重写。
final关键字
final关键字是最终的意思,可以修饰类,成员变量,成员方法。
特点:
a.修饰方法,方法不能被重写
例:当我们不想让子类覆盖重写父类中的方法或者功能,只能让子类去使用时,可以使用final
class Father7{
public final void show(){ //使用final修饰方法
System.out.println("父类中的show方法");
}
}
class Son7 extends Father7{
@Override
public void show(){ //子类不能重写,否则报错
System.out.println("这是子类中的show方法");
}
}
public class Extends7 {
public static void main(String[] args) {
Son7 son7 = new Son7();
son7.show();
}
}
可以看到子类如果重写了父类用final修饰的方法,运行就会报错。
b.修饰类,类不能被继承
c.修饰变量,变量就变成了常量,只能被赋值一次(在构造方法完毕之前赋值即可)
final修饰局部变量
a. 在方法内部,该变量不可以被改变
b. 在方法声明上,基本类型和引用类型作为参数的情况
基本类型,是值不能被改变
引用类型,是地址值不能被改变(该对象堆内存中的值是可以改变的)
c.final修饰变量的初始化时机
在对象构造完毕前即可
面向对象三大特征——封装https://blog.csdn.net/qq_52400447/article/details/122520149