JAVA面向对象

面向对象和面向过程的区别有哪些?分别有什么优缺点?

面向过程将问题分解成步骤,按照步骤实现函数,并依次调用,数据和对数据的操作是分离的。面向对象将问题分解成对象,描述事物的行为,对象与属性和行为是关联的。

面向过程的优点是性能高,缺点是不容易维护、复用和扩展。面向对象的优点是具有封装、继承、多态的特性,因而容易维护、复用和扩展,缺点是性能低。

对象和类之间有哪些联系?

对象是具体的实例,有自己独有的状态和行为。类是对具有相同特征的对象的抽象,用来定义对象的状态和行为。可以从一个类创建多个对象。

静态和实例的区别

Java 的类成员(成员变量、方法等)可以是静态的或实例的。使用关键字 static 修饰的类成员是静态的类成员,不使用关键字 static 修饰的类成员则是实例的类成员。
1.外部调用

从外部调用静态的类成员时,可以通过类名调用,也可以通过对象名调用。从外部调用实例的类成员,则只能通过对
象名调用。

例如对于字符串类型 String,方法 format 是静态的,可以通过 String.format 调用,而方法 length 是
实例的,只能通过 str.length 调用,其中 str 是 String 类型的实例。

建议通过类名调用静态的类成员,因为通过类名调用静态的类成员是不需要创建对象的,而且可以提高代码的可读性。

2.内部访问

静态方法只能访问静态的类成员,不能访问实例的类成员。实例方法既可以访问实例的类成员,也可以访问静态的类
成员。

为什么静态方法不能访问实例的类成员呢?因为实例的类成员是依赖于具体对象(实例)的,而静态方法不依赖于任
何实例,因此不存在静态方法直接或间接地访问实例或实例的类成员的情况。

判断使用静态或实例

如何判断一个类成员应该被定义成静态的还是实例的呢?取决于类成员是否依赖于具体实例。如果一个类成员依赖于
具体实例,则该类成员应该被定义成实例的类成员,否则就应该被定义成静态的类成员。

例如对于字符串类 String,考虑方法 format 和方法 length。

方法 format 的作用是创建格式化的字符串,该方法不依赖于任何 String 的实例,因此是静态方法(类成员)。

方法 length 的作用是获得字符串的长度,由于字符串的长度依赖于具体字符串,因此该方法依赖于 String 的
实例,是实例方法(类成员)。

对于数学类 Math,所有的类成员都不依赖于具体的实例,因此都被定义成静态的类成员。

初始化块

代码初始化块属于类成员,在加载类时或创建对象时会隐式调用代码初始块。使用初始化块的好处是可以减少多个构造器内的重复代码。

初始化块的分类

初始化块可以分成静态初始化块和非静态初始化块,前者在加载类时被隐式调用,后者在创建对象时被隐式调用。
单个类的初始化块的执行顺序

如果有初始化块,则初始化块会在其他代码之前被执行。具体而言,静态初始化块会在静态方法之前被执行,非静态
初始化块会在构造器和实例方法之前被执行。

由于静态初始化块在加载类时被调用,因此静态初始化块会最先执行,且只会执行一次。

由于非静态初始化块在创建对象时被调用,因此每次创建对象时都会执行非静态初始化块以及执行构造器。非静态初
始化块的执行在静态初始化块的执行之后、构造器的执行之前

存在继承关系的初始化块的执行顺序

如果存在继承关系,则在对子类进行类的加载和创建对象时,也会对父类进行类的加载和创建对象。执行顺序仍然是
静态初始化块、非静态初始化块、构造器,由于存在继承关系,因此情况较为复杂。

对于两个类的情况,即一个父类和一个子类,执行顺序如下。

1.执行父类的静态初始化块。
2.执行子类的静态初始化块。
3.执行父类的非静态初始化块。
4.执行父类的构造器。
5.执行子类的非静态初始化块。
6.执行子类的构造器。

更一般的情况,对于多个类之间的继承关系(可能超过两个类,例如 B 继承了 A,C 继承了 B),执行顺序如下。

1.按照从父类到子类的顺序,依次执行每个类的静态初始化块。
2.按照从父类到子类的顺序,对于每个类,依次执行非静态初始化块和构造器,然后执行子类的非静态初始化块和构
造器,直到所有类执行完毕

静态初始化块和非静态初始化块的执行顺序是什么,以及分别会执行多少次?

静态初始化块的优先级最高,会最先执行,在非静态初始化块之前执行。静态初始化块只在类第一次被加载时执行,
非静态初始化块在每次创建对象时执行一次,因此创建了多少个对象,就会执行多少次非静态初始化块。

关键字 this

关键字 this 代表当前对象的引用。当前对象指的是调用类中的属性或方法的对象。

关键字 this 用于引用隐藏变量

在方法和构造方法中,可能将属性名用作参数名,在这种情况下,需要引用隐藏的属性名才能给属性设置新值。例如
,当属性名和参数名都是 var 时,需要通过 this.var = var 对属性进行赋值。

当方法内部有局部变量和属性名相同时,同样需要通过关键字 this 引用对象的属性。

如果方法内部不存在和属性名相同的局部变量,则在使用属性时,属性前面的 this 可以省略。

关键字 this 用于调用其他构造方法

在构造方法中,可以通过关键字 this 调用其他构造方法,具体用法是 this(参数列表)。

Java 要求,在构造方法中如果使用关键字 this 调用其他构造方法,则 this(参数列表) 语句必须出现在其他
语句之前。

关键字 this 不能在静态代码块中使用

由于关键字 this 代表的是对象的引用,因此依赖于具体对象,而静态方法和静态初始化块不依赖于类的具体对象
,因此静态方法和静态初始化块中不能使用关键字 this。

关键字 this 在方法中以及在构造方法中分别表示什么?

关键字 this 可以用于引用对象的属性,在方法和构造方法中都可以通过关键字 this 引用对象的属性。在构造方
法中,可以通过关键字 this 调用其他构造方法。

关键字 this 是否可以在静态方法中使用?说明理由。

关键字 this 不可以在静态方法中使用。关键字 this 代表的是对象的引用,而静态方法不依赖于类的具体对象。

this和super

this关键字和super关键字
1)this关键字
    1. this关键字用于引用隐藏变量,如方法名与参数名一致,使用this显示调用;
    2. this关键字用于调用本类其它构造方法,方式:this(参数名),用在第一行
    3. this关键字不能用在静态方法中;
2)super关键字
    1. 子类中访问父类的方法和属性;(super.属性名)
    2. 子类构造方法中访问父类的构造方法(super(参数),用在第一行)
3)二者的区别
    1)代表事物不一样:this代表的是当前函数对应的对象,super代表的是父类空间的引用;
    2)使用前提不一致:this不需要继承关系,super需要在继承中使用;
    3)调用构造方法区别:this用于调用本类中其它构造方法,super用于调用父类的构造方法,都在第一行。
4)this和super不能同时出现在同一个构造方法中去调用其它构造方法;

可见性修饰符和数据域封装

Java 的可见性修饰符用于控制对类成员的访问。可见性修饰符包括 public、private、protected 和默认修饰符,此处介绍 public、private 和默认修饰符,protected 将在继承和多态部分介绍。
不同的可见性修饰符的含义

可见性修饰符 public 表示类成员可以在任何类中访问。

可见性修饰符 private 表示类成员只能从自身所在的类中访问。

如果不加任何可见性修饰符,则称为默认修饰符,表示类成员可以在同一个包里的任何类中访问,此时也称为包私有
或包内访问。

数据域封装
可见性修饰符可以用于控制对类成员的访问,也可以用于对数据域进行封装。

数据域封装的含义是,对数据域使用 private 修饰符,将数据域声明为私有域。如果不使用数据域封装,则数据域
的值可以从类的外部直接修改,导致数据被篡改以及类难以维护。使用数据域封装的目的是为了避免直接修改数据域
的值。

在定义私有数据域的类之外,不能通过直接引用的方式访问该私有数据域,但是仍然可能需要读取和修改数据域的值。
为了能够读取私有数据域的值,可以编写 get 方法(称为读取器或访问器)返回数据域的值。为了能够修改私有数
据域的值,可以编写 set 方法(称为设置器或修改器)将数据域的值设置为新值

字符串

字符串是常用的数据类型。在 Java 中,常见的字符串类型包括 String、StringBuffer 和 StringBuilder。

String

从 String 的源码可以看到,String 使用数组存储字符串的内容,数组使用关键词 final 修饰,因此数组内容
不可变,使用 String 定义的字符串的值也是不可变的。

由于 String 类型的值不可变,因此每次对 String 的修改操作都会创建新的 String 对象,导致效率低下且占
用大量内存空间。

StringBuffer 和 StringBuilder

StringBuffer 和 StringBuilder 都是 AbstractStringBuilder 的子类,同样使用数组存储字符串的内容,由于数组没有使用关键词
 final 修饰,因此数组内容可变,StringBuffer 和 StringBuilder 都是可变类型,可以对字符串的内容进行修改,且不会因为修改而
 创建新的对象。

在需要经常对字符串的内容进行修改的情况下,应使用 StringBuffer 或 StringBuilder,在时间和空间方面都显著优于 String。

StringBuffer 和 StringBuilder 有哪些区别呢?从源码可以看到,StringBuffer 对定义的方法或者调用的方法使用了关键词
 synchronized 修饰,而 StringBuilder 的方法没有使用关键词 synchronized 修饰。由于 StringBuffer 对方法加了同步锁,
 因此其效率略低于 StringBuilder,但是在多线程的环境下,StringBuilder 不能保证线程安全,因此 StringBuffer 是更优的选择。

总结

1.String 是不可变类型,每次对 String 的修改操作都会创建新的 String 对象,导致效率低下且占用大量内存空间,因此 String 适用
于字符串常量的情形,不适合需要对字符串进行大量修改的情形。

2.StringBuffer 是可变类型,可以修改字符串的内容且不会创建新的对象,且 StringBuffer 是线程安全的,适用于多线程环境。

3.StringBuilder 是可变类型,与 StringBuffer 相似,在单线程环境下 StringBuilder 的效率略高于 StringBuffer,但是在多
线程环境下 StringBuilder 不保证线程安全,因此 StringBuilder 不适合多线程环境。

继承

关键字 super

关键字 super 指向当前类的的父类。关键字 super 可以用于两种途径,一是调用父类的构造方法,二是调用父类
的方法。

调用父类的构造方法,使用 super() 或 super(参数),该语句必须是子类构造方法的第一个语句,且这是调用父
类构造方	法的唯一方式。

调用父类的方法,使用 super.方法名(参数)。

构造方法链

如果构造方法没有显式地调用同一个类中其他的构造方法或父类的构造方法,将隐性地调用父类的无参数构造方法,
即编译器会把 super() 作为构造方法的第一个语句。

构造一个类的实例时,将会沿着继承链调用所有父类的构造方法,父类的构造方法在子类的构造方法之前调用,称为
构造方法链。

方法的重写

子类从父类中继承方法。如果子类修改了父类中定义的方法,则称为方法重写。方法重写要求子类的方
法和父类的方法的签名相同。

如果方法的返回值类型是基本数据类型或者 void,则要求子类的方法的返回值类型和父类的方法的返回值类型相同。
如果方法的返回值类型是引用类型,则要求返回值类型相同或者子类的方法的返回值类型是父类的方法的返回值类
型的子类。

实例方法只有当可访问时才能被重写。由于私有方法不能在定义该方法的类外访问,因此私有方法不能被重写。

静态方法可以被继承,但是不能被重写

可见性修饰符 protected

之前已经介绍了三种可见性修饰符:public、private 和默认修饰符,此处介绍第四种可见性修饰符:protected。

可见性修饰符 protected 的可见性在 public 和默认之间,表示类成员可以在同一个包里的任何类中访问,也可
以在该类的子类中访问。

子类可以覆盖父类的 protected 方法,并把该方法的可见性改成 public。但是子类不能降低父类方法的可见性,即不能把父类的 public 方法的可见性改成 protected。
关键字 final

关键字 final 可以用于声明常量,表示常量不会改变。

关键字 final 也可以用于修饰类和方法。使用 final 修饰的类是终极类,不能被继承。使用 final 修饰的方
法不能被子类重写。

例如,String、StringBuffer 和 StringBuilder 类都使用了关键字 final 修饰,因此这些类不能被继承。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值