Java面向对象
类和对象
1.面向对象和面向过程
面向过程:面向过程性能比面向对象高(后面会补充说明为什么)
类调用时需要实例化,开销大,比较消耗资源,当性能是最重要的考虑因素的时候(单片机,Linux)——面向过程没有面向对象易维护,易复用,易扩展
面向对象:面向对象易维护,易复用,易扩展。因为:面向对象有封装,继承,多态性的特性。可以设计出低耦合的的系统,系统更加灵活,更加易于维护。
补充:
为什么面向过程性能比面向对象高?
面向过程需要分配内存,计算内存偏移量,Java性能差的主要原因是因为Java是半编译语言,最终的执行代码不能直接被CPU执行的二进制机械码。面向过程语言大多都是直接编译成机械码在电脑上执行的,并且其他的面向过程的脚本语言性能也不一定比Java好。
2.构造器能否被重写?
构造器是constructor
重写是override
重载是overload
构造器不能被重写,可以被重载,所以存在一个类中有多个构造函数的情况。
3.Java中定义一个不做事且没有参数的构造方法
Java程序在执行子类的构造方法之前,如果没用super()来调用父类特定的构造方法,那么会调用父类中”没有参数的构造方法“。
如果父类中只定义了有参数的构造方法,而在子类的构造方法中没用用super()来调用父类中特定的构造方法,则编译时会发生错误,因为Java程序在父类中找不到没用参数的方法可以执行。
4.成员变量和局部变量
区别点 | 成员变量 | 局部变量 |
---|---|---|
语法形式 | 属于类,可以被public,private,static等修饰符修饰,可以被final修饰 | 方法中定义的变量或是方法的参数,不能被访问控制修饰符及static所修饰,可以被final修饰 |
变量在内存中的存储方式 | 如果使用static修饰,那么这个成员变量属于类,如果没用使用static修饰,这个成员变量属于实例,对象存在于堆内存 | 局部变量存在于栈内存 |
变量在内存中的生存时间 | 对象的一部分,随着对象的创建而存在 | 随着方法的调用而自动消失 |
赋值区别 | 如果没用被赋初值,则会自动以类型的默认值而赋值(例外:被final修饰的成员变量必须显式地赋值) | 不会自动赋值 |
5.创建一个对象
用new运算符创建对象实例,实例是存在堆内存中。
对象引用:指向对象实例,对象引用也存放在栈内存中。
一个对象引用可以指向0或1个对象;一个对象可以有n个引用指向它
6.类的构造方法
类的构造方法主要用于完成对类对象的初始化工作。一个类没有声明构造方法也可以执行,因为存在默认的不带参数的构造方法。当程序员自己添加了类的构造方法时,Java就不会默认再添加无参数的构造方法。
此时,不能直接new一个对象而不传递参数,就算时无参的构造方法,也需要在创建对象的后面加一个括号。当重载了有参的构造方法时,也需要把无参的构造方法写出来。
7.构造方法的特性
- 名字与类名相同
- 没有返回值,但不能用void声明构造函数
- 生成类的对象时自动执行,不需要调用
8.面向对象的三大特征
在此之前,先补充两句话。
- 在调用子类的构造方法之前先调用父类没有参数的构造方法,是为了帮助子类做初始化工作。
- 对象的相等指的是内存中存放的内容是否相等;引用的相等指的是指向的内存地址是否相等。
封装
含义:把一个对象的状态信息,就是对象的属性隐藏在对象内部,不允许外部对象直接访问对象的内部信息。可以提供一些被外界访问的方法来操作属性。
如果一个类不提供给外界访问的方法,那么这个类就没有什么意义了。
继承
含义:不同类型的对象,相互之间经常有一定数量的共同点。同时,每一个对象定义了额外的特性使得与众不同。继承是使用已经存在的类的定义作为基础建立新类的技术,新类的定义可以增加新的数据或新的功能,也可以使用父类的功能,但是不能选择性地继承父类。
优点:可以快速创建新的类,提高代码地重用,程序地可维护性,节省大量创建新类地时间,提高开发效率。
注意:
- 子类拥有父类对象所有地属性和方法(包括私有属性和私有方法),但是父类中私有属性和方法,子类是无法访问。
- 子类可以对父类进行扩展,拥有自己地属性和方法。
- 子类可以用自己地方法实现父类地方法。
多态
含义:表示一个对象具有多种地状态。具体表现为父类地引用指向子类地实例。
特点:
- 对象类型和引用类型之间具有继承类和实现接口地关系
- 对象类型不可变,引用类型可以变
- 方法具有多态性,属性不具有多态性
- 引用类型变量发出的方法调用到底是哪个类中地方法,必须在程序运行期间才能确定。
- 多态不能调用”只在子类中存在但是父类不存在“地方法
- 当子类重写了父类地方法,执行地是子类覆盖地方法,当子类没有覆盖父类地方法,则执行地是父类地方法。
总结:多态是同一个行为具有多个不同表现形式或形态的能力。多态就是同一个接口,使用不同的实例而执行不同操作。
多态存在的三个必要条件
继承
重写
父类引用指向子类对象:(例如)
S p = new SS();
9.修饰符
- 静态方法可以不通过对象进行调用,所以在静态方法里面,不能调用其他的非静态变量,也不可以访问非静态变量成员。
静态成员变量和非静态成员变量的主要区别
区别角度 | 静态成员变量 | 非静态成员变量 |
---|---|---|
保存位置 | 方法区的静态区域 | 堆内存中的对象空间中 |
书写格式 | 数据类型前面多了static | 没有static |
生命周期 | 类加载完成,就分配完空间;类被卸载时空间被回收 | 创建对象时分配空间;对象变为垃圾空间被回收的时候被销毁 |
使用方法 | 通过类名使用 | 通过对象使用 |
修改后的影响范围 | 对该类的所以对象都有影响 | 只对一个对象有影响 |
- 静态方法和实例方法的不同
外部调用静态方法时,可以使用”类名.方法名“的方法,也可以使用”对象名.方法名“的方法;而实例方法只有”对象名.方法名“。调用静态方法可以无需创建对象。
静态方法在访问本类的成员时,只允许访问静态成员(即静态成员变量和静态方法),而不允许访问实例成员变量和实例方法;实例方法没有这个限制。
10.final,static,this,super 关键字
final 关键字
主要用在三个地方:变量、方法、类
对于一个final变量,如果是基本数据类型的变量,则其数值一旦在初始化之后便不能更改;如果是引用类型的变量,则在对其初始化之后便不能再让其指向另一个对象。
当用final修饰一个类时,表明这个类不能被继承。final类中的所有成员方法都会被隐式地指定为final方法。
使用final方法的原因
- 把方法锁定,以防任何继承类修改它的含义
- 效率。在早期的Java实现版本中,会将final方法转为内嵌调用。但是如果方法过于庞大,可能看不到内嵌调用带来的任何性能提升(现在的Java版本已经不需要使用final方法进行这些优化了)。类中所有的private方法都隐式地指定为final
static 关键字
static 关键字主要有4种场景使用:
修饰成员变量和成员方法
静态代码块
静态内部类(static修饰类的话只能修饰内部类)
静态导包(用来导入类中的静态资源,1.5之后的新特性)
- 被 static 修饰的成员属于类,不属于单个这个类的某个对象,被类中所有对象共享,可以并且建议通过类名调用。被static 声明的成员变量属于静态成员变量,静态变量 存放在 Java 内存区域的方法区。
调用格式:
类名.静态变量名
类名.静态方法名() - 静态代码块定义在类中方法外, 静态代码块在非静态代码块之前执行(静态代码块—>非静态代码块—>构造方法)。 该类不管创建多少对象,静态代码块只执行一次
- 静态内部类与非静态内部类之间存在一个最大的区别: 非静态内部类在编译完成之后会隐含地保存着一个引用,该引用是指向创建它的外围类,但是静态内部类却没有。没有这个引用就意味着:1. 它的创建是不需要依赖外围类的创建。2. 它不能使用任何外围类的非static成员变量和方法。
- 格式为:import static 这两个关键字连用可以指定导入某个类中的指定静态资源,并且不需要使用类名调用类中静态成员,可以直接使用类中静态成员变量和成员方法。
this 关键字
this关键字用于引用类的当前实例
super 关键字
super关键字用于从子类访问父类的变量和方法
使用 this 和 super 要注意的问题
在构造器中使用 super() 调用父类中的其他构造方法时,该语句必须处于构造器的首行,否则编译器会报错。
this 调用本类中的其他构造方法时,也要放在首行。
this、super不能用在static方法中。
11.接口和抽象类
- 接口方法默认public,所有方法在接口中不能有实现;抽象类可以有非抽象的方法
- 接口种除了static,final变量,不能有其他变量,而抽象类中不一定
- 一个类可以实现多个接口,但只能实现一个抽象类;接口本身可以通过extends关键字扩展多个接口
- 接口方法默认修饰符是public,抽象方法可以有public,protected,default,抽象方法是为了被重写所以不能使用private关键字
- 从设计层面上说,抽象是对类的抽象,是一种模板设计;接口是对行为的抽象,是一种行为规范
注:
JDK8中,接口也可以定义静态方法,可以直接用接口名调用。如果同时实现两个接口,接口中定义了一样的默认方法,则必须进行重写。
JDK9中的接口被允许定义私有方法
JDK7——接口中只能由常量变量和抽象方法,这些接口方法必须由选择实现接口的类实现
JDK8——接口可以有默认方法和静态方法
JDK9——接口中引入了私有方法和私有静态方法
12.String,StringBuffer,StringBuilder
String类中使用final关键字修饰字符数组来保存字符串
private final char value[]——String对象不可变
(在Java9以后,String类的实现用byte数组存储字符串:private byte[] value)
StringBuilder,StringBuffer都继承于AbstractStringBuilder类,在父类中也是使用字符数组保存字符串char[]value ,没用final关键字修饰,所以这两个对象都可变
这两个的构造方法也是调用父类构造方法实现的
根据线程是否安全
String中对象不可变,线程安全
StringBuffer对方法加了同步锁或者对调用的方法加了同步锁,线程安全
StringBuilder没有对方法加同步锁,线程不安全
根据性能
String类型进行改变时,会生成一个新的String对象,将指针指向新的String对象
StringBuffer每次都会对StringBuffer对象本身进行操作,而不是生成新的对象并改变对象引用;
相同情况下,StringBuilder性能比StringBuffer高10%-15%,但是需要冒多线程不安全的风险
总结:
- 操作少量数据用String
- 单线程操作字符串缓冲区下操作大量数据用StringBuilder
- 多线程操作字符串缓冲区下操作大量数据用StringBuffer
13.Object类常见方法(不用背,多看看熟悉一下)
- public final native Class<?> getClass()
返回当前运行时对象的Class对象,使用了final关键字修饰,不允许子类重写 - public native int hashCode()
返回对象的哈希码,用在哈希表中,例如JDK中的HashMap - public boolean equals(Object obj)
比较两个对象的内存地址是否相等,String类对该方法进行了重写,用户比较字符串的值是否相等 - protected native Object clone() throws CloneNotSupportedException
创建并返回当前对象的一份拷贝。一般对于x.clone()!=x为真,x.clone().getClass()==x.getClass()为真
Object本身没有实现Cloneable接口,所以不重写clone方法并且进行调用的话会发生CloneNotSupportedException异常 - public String toString()
返回类的名字@实例的哈希码的16进制的字符串 - public final native void notify()
不能被重写的本地方法。唤醒一个对此对象监视器上等待的线程,如果有多个线程在等待只会任意唤醒一个 - public final native void notfyAll()
跟notify一样,区别是会唤醒在此对象监视器上等待的所有线程,而不是一个线程 - public final native void wait(long timeout) throws InterruptException
暂停线程的执行。(sleep方法没有释放锁),wait方法释放锁。timout是等待时间 - public final void wait(long timeout,int nanos) throws InterruptedException
多了一个参数,这个参数表示额外时间(单位是毫微秒,0-999999)超时的时间还需要加上nanos毫秒 - public final void wait() throws InterruptedException
跟之前两个wait方法一样,但是该方法一直在等待,没有超时时间的概念 - protected void finalize() throws Throwable{}
实例被垃圾回收器回收的时候触发的操作