面向对象
对象和类之间的关系
对象:
对象是人们要进行研究任何事物,一切事物都可以认为是对象。
对象具有状态和行为:
对象具有状态,比如姓名,年龄,性别等。
对象还有操作,比如吃饭,睡觉,写代码等。
类:
具有相同特性(状态)和行为(功能)的对象的抽象就是类。因此,对象的抽象是类,类的具体化就是对象,也可以说类的实例是对象,类实际上就是一种数据类型。
类具有特性,对象的状态,用成员变量来描述,封装数据。
类具有功能,对象的行为,用方法来描述。
理解:对象是类的实例,类是对象的模板。
对象的操作和创建
- 根据类来创建对象的语法:
类名 变量 = new 类名(); - 给字段设置值:
对象变量.字段名 = 该类型的值; - 获取字段的值:
该字段类型 变量 = 对象变量.字段值; - 通过对象调用方法.
对象变量.方法(实参);
构造器
构造器的作用:
- 创建对象,必须和 new 一起使用
- 完成对象的初始化操作
构造器的特点:
- 构造器的名称和当前所在类的名称相同
- 禁止定义返回类型,千万不要使用void作为返回类型.
- 在构造器中,不需要使用return语句
static关键字
static修饰的资源属于类级别,而不是对象级别。
static修饰符的特点:
- static修饰的成员(字段/方法),随着所在类的加载而加载.
- 优先于对象的存在.
- satic修饰的成员被该类型的所有对象所共享.
- 直接使用类名访问static成员
那么,static的方法可以调用static修饰的方法,不能调用没有用static修饰的方法
没有static修饰的方法可以调用static修饰的方法
什么时候定义成static的字段和方法?
如果这个一个状态/行为属于整个事物(类),就直接使用static修饰.被所有对象所共享.
各种类型变量的比较
存在位置 | 生命周期开始 | 生命周期结束 | 在内存中的位置 | |
---|---|---|---|---|
类变量 | 类中字段,使用static修饰 | 字节码文件被加载进JVM | JVM停止 | 方法区 |
实例变量 | 类中字段,没有使用static修饰 | 创建所在类的对象时 | 该对象被GC回收 | 堆 |
局部变量 | 方法形参,代码块中,方法内 | 代码执行到初始化变量时 | 所在方法/代码块结束 | 当前方法的栈帧 |
局部变量定义后,必须显示初始化后才能使用,因为系统不会为局部变量执行初始化操作。这就意味着,定义局部变量后,系统并未为这个变量分配内存空间。
直到程序为这个变量赋值时,系统才会为局部变量分配内存,并将初始值保存到该内存中。
局部变量不属于任何类或实例,因此它总是保存在其所在方法的栈内存中。
基本数据局部变量:直接把这个变量的值保存到该变量所对应的内存中。
引用数据局部变量:这个变量内存中存的是地址,通过该地址引用到该变量实际引用堆里的的对象。
栈内存中的变量无需系统垃圾回收,其往往随方法或代码块的运行结束而结束。
开发中应尽量缩小变量的作用范围
代码块
在类或者在方法中,直接使用"{}"括起来的一段代码,表示一块代码区域。
代码块里变量属于局部变量,只在自己所在区域(前后的{})内有效。
根据代码块定义的位置的不同,我们又分成三种形式:
局部代码块:直接定义在方法内部的代码块:
一般的,我们是不会直接使用局部代码块的,只不过我们会结合if,while,for,try等关键字联合,表示一块代码区域.
初始化代码块(构造代码块): 直接定义在类中
每次创建对象的时候都会执行初始化代码块,每次创建对象都会调用构造器,在调用构造器之前,会先执行本类中的初始化代码块
**静态代码块:**使用static修饰的初始化代码块
在主方法执行之前执行静态代码块,且只执行一次 main是程序主入口,为什么静态代码块会优先于main执行?
静态成员随着字节码的加载也加载进JVM,此时main方法还没执行,因为方法需要JVM调用.
先把字节码加载进JVM, 而后JVM再调用main方法.
封装
- 把对象的状态和行为看成一个统一的整体,将二者存放在一个独立的模块中(类);
- “信息隐藏”, 把不需要让外界知道的信息隐藏起来,尽可能隐藏对象功能实现细节,向外暴露方法,保证外界安全访问功能; 把所有的字段使用private私有化,不准外界访问,把方法使用public修饰,允许外界访问.
把所有数据信息隐藏起来,尽可能隐藏多的功能,只向外暴露便捷的方法,以供调用.
封装的好处:
- 使调用者正确,方便地使用系统功能,防止调用者随意修改系统属性.
- 提高组件的重用性.
- 达到组件之间的低耦合性(当某一个模块实现发生变化时,只要对外暴露的接口不变,就不会影响到其他模块).
访问修饰符
修饰符 | 类内部 | 同一包下 | 子类 | 任何地方 |
---|---|---|---|---|
private | √ | |||
无 | √ | √ | ||
protected | √ | √ | ||
public | √ | √ | √ | √ |
this关键字
this表示当前对象
this主要存在于两个位置
- 构造器中:表示当前创造的对象
- 方法中:哪一个对象调用this所在的方法,this就指代的是哪个对象
使用this可以
- 解决成员变量和局部变量的二义性(防止混淆同名变量)
- 一个类中实例方法的互相调用(可以省略this)
- 构造器重载的互相调用,this([参数]) 必须写在构造方法第一行
- static不能和this一起使用 (当字节码文件被加载进JVM,static成员就产生了,此时还没有创建对象)
继承
基于某个父类对对象的定义加以拓展,而产生新的子类定义,子类可以继承父类原来的某些定义,也可以增加原来父类所没有的定义,或者覆写父类中的某些特性。
父类存放共性,子类存放特性,子类继承父类的共性,解决了代码的重复问题
继承关系的真正作用:表示出一个体系
子类可以继承父类哪些东西
- 如果父类中的成员使用**public修饰,子类可以继承**.
- 如果父类中的成员使用**protected修饰,子类也可继承**,即使父类和子类不在同一个包中.
- 如果父类和子类在**同一个包中,此时子类可以继承父类中缺省修饰符的成员**.
- 如果父类中的成员使用private修饰,子类打死都继承不到.因为private只能在本类中访问.
- 父类的构造器,子类也不能继承,因为构造器必须和当前的类名相同
super关键字
使用场景:
- 在子类方法中,调用父类被覆盖的方法,此时必须使用super.
- 在子类构造器中,调用父类构造器,此时必须使用super语句且必须写在第一行 (默认是有的,不用自己再写)
子类初始化过程:
在创建子类对象之前,会先创建父类对象.调用子类构造器之前,在子类构造器中会先调用父类的构造器,默认调用的是父类无参数构造器
1): 如果父类不存在可以被子类访问的构造器,则不能存在子类.
2):如果父类没有提供无参数构造器,此时子类必须显示通过super语句去调用父类带参数的构造器.
static不能和this super共存
实例解析
子类继承不到父类的private字段, 但是可以继承父类的getset方法 通过这些getset方法加上调用(super)父类的带参的构造器
通过构造器我们可以给子类赋值 ,这样也就间接的继承到了父类的私有字段 new相当于创建父类对象 引用的还是父类属性
而**子类无需再定义这些字段**
方法重写 (override)与重载(overload)
重写:一同两小一大
- 在父子类继承当中
- 实例**方法签名**相同(方法签名=方法名+参数列表)
- 子类方法的**返回值类型**是和父类方法的返回类型相同或者是其子类(小)
- 子类方法声明抛出的**异常类型**和父类方法声明抛出的异常类型相同或者是其子类(小)
- 子类方法的**访问权限比父类方法访问权 限更大**或相等
重载: 一同两不同
- 一个类中**方法名**相同
- 参数**类型和个数**不同
final
可以修饰类 属性和方法 表示最终的
被final修饰的类是一个最终类
- 不能被继承
被final修饰的变量是一个常量
- 必须初始化才能使用
- 不能重新赋值
被final修饰的方法
- 不能被重写
什么时候可以把一个类设计成final类?
只要满足一下任意一个条件即可:
1.某类不是专门为了继承而设计
2.出于安全考虑,类的实现细节不许再修改,不准修改源代码
3.确信该类不会再被拓展
**final修饰基本类型变量:**表示该变量的值不能改变,即不能用“=”号重新赋值
**final修饰引用类型变量:**表示该变量的引用的地址不能变,而不是引用地址里的内容不能变。
组合关系
继承关系: 子类可以继承到父类中部分的成员,那么此时子类是可以修改到父类的信息的.
继承关系破坏封装,为了复用代码可能会让子类具有不该具有的功能.
如果A类为了得到B类的功能行为:
A类如果是B类的一种特殊情况,使用继承,否则使用组合关系
多态
同一事物下的 不同表现形态
前提:
-
必须要有**继承或实现**关系
-
必须要有方法的重写
-
子类的实例赋值给父类类型的变量
//Animal是Cat父类 new一个子类对象 赋值给Animal类型的cat变量
Animal cat=new Cat();
// 编译类型: 声明对象变量的类型,Animal,表示把对象看出什么类型.
// 运行类型: 对象的真实类型,Cat 运行类型就是对象的真实类型
```
当编译类型和运行类型不同的时候,多态就出现了
多态的**特点**: 把子类对象赋给父类变量,在运行时期会表现出具体的子类特征(调用子类的方法).
多态的**作用:当把不同的子类对象都当作父类类型来看待,可以屏蔽不同子类对象之间的实现差异**,从而写出通用的代码达到==通用编程==,以适应需求的不断变化。
引用类型转换
基本类型转换
自动类型转换: 把小类型的数据 赋给 大类型的变量. (此时的大和小表示的容量范围)
byte b = 12; byte是1个字节
int i = b; int是4个字节
强制类型转换: 把大类型的数据赋给 小类型的变量.
short s = (short) i ;short是2个字节
引用类型的转换
引用类型的大和小,指的是 父类 和子类的关系.
自动类型转换: 把子类对象赋给父类变量(多态).
Animal a = new Dog();
Object是所有类的根类:
Object obj = new Dog();
强制类型转换: 把父类类型对象赋给子类类型变量(当时该父类类型变量的真实类型应该是子类类型).
Animal a = new Dog();
Dog d = (Dog)a;
instanceof 运算符: 判断该对象是否是某一个类的实例.
语法格式:boolean b = 对象A instanceof 类B; // 判断 A对象是否是 B类的实例,如果是,返回true.
多态与类中变量和方法的关系
多态时,是调用不到子类独有的方法和字段的
测试方法
class testClass{
public static void main(String[] args) {
SuperClass subClass = new SubClass();
subClass.doWork();
}
}
//下图分情况讨论结果
字段不存在多态特性
通过对象调用字段,在**编译时期**就已经决定了要调用哪一块内存空间的数据
字段不存在重写的概念,在多态时,不能有多态的特征即在运行时期不能体现子类的特征(属性);
实例解析
工具类的封装
**设计模式:**是一套被反复使用、多数人知晓的、经过分类编目的、代码设计经验的总结。使用设计模式是为了可重用代码、让代码更容易被他人理解、保证代码可靠性
工具类如何设计: 工具在开发中其实只需要存在一份即可.
1):如果工具方法没有使用static修饰,说明工具方法得使用工具类的对象来调用.
此时把工具类设计为单例的.
2):如果工具方法全部使用static修饰,说明工具方法只需要使用工具类名调用即可.
此时必须把工具类的构造器私有化(防止创建工具类对象调用静态方法).
建议使用第二种
单例模式(饿汉式)
abstract关键字
使用abstract修饰且没有方法体的方法,称为抽象方法。
抽象方法的特点:
- 使用抽象abstract修饰,方法没有方法体,留给子类去实现/覆盖
- 抽象方法修饰符**不能是**private 和 final以及static
- 抽象方法必须定义在抽象类或接口中
使用abstract修饰类,该类就是抽象类
抽象类的特点:
- 不能创建实例即不能new一个抽象类,即使创建出抽象类对象,调用抽象方法,根本没有方法体。(调用父类方法,可以继承父类)
- **抽象类是有构造器的,**子类构造器必须先调用父类构造器,可以不包含抽象方法,若一旦包含,该类必须作为抽象类,抽象类可以包含普通方法,构造器可以传递参数用。
- 若子类没有实现/覆盖父类所有的抽象方法,那么子类也得作为抽象类(抽象派生类)。
- 构造方法不能都定义成私有的,否则不能有子类
- **抽象类不能使用final修饰,**因为必须要有子类 子类再去实现抽象方法,被final修饰就不能有子类
- 抽象类是不完整的类,需作为父类(必须要有子类),功能才能得以实现
接口
定义接口语法: [public] interface 接口名{}
成功编译之后,和类一样,具有一份字节码
接口只定义了类应当遵循的规范,却不关心这些类的内部数据和其功能的实现细节。
理解:接口是更严格的抽象类
因为抽象类里有抽象方法和非抽象方法,接口只能有抽象方法
接口的特点:
- 没有构造方法,也不能显示定义构造器,不能实例化。
- 接口只能继承接口,不能继承类,且接口支持多继承(类是单继承关系)。
- 接口里的方法方法全是抽象的,默认修饰符是public abstract。
- 接口里的字段全是全局静态常量,默认修饰符是public static final。
- 接口里的内部类全是公共静态的,默认修饰符是public static。
接口中的成员
-
接口中没有构造器,推论:接口不能创建对象(不能 new ),接口中不能定义普通方法.
-
接口中定义的成员变量,实质是全局静态常量,默认使用 public static final来修饰.
public static final String NAME = "张三";
-
接口中定义的方法都是公共的抽象方法,默认的使用 public abstract 来修饰方法.
public abstract void walk();
一般的,我们在接口中定义方法,不喜欢使用修饰符.
-
接口中定义的内部类都是公共的静态内部类,默认使用public static来修饰内部类.
public static interface ABC{}
接口中的多态
其实,接口只是特殊的类,弥补了java只能单继承的缺陷
常用类
Object
Object类中常见的方法
-
protected void finalize() :当垃圾回收器确定不存在对该对象的更多引用时,由对象的垃圾回收器调用此方法。
垃圾回收器在回收某一个对象之前,会先调用该方法,做扫尾操作. 该方法我们不要去调用. -
Class getClass() :返回当前对象的真实类型。即拿到.class文件
-
int hashCode(): 返回该对象的哈希码值,hashCode决定了对象再哈希表中的存储位置,不同对象的hashCode是不一样的.
-
boolean equals(Object obj): 拿当前对象(this)和参数obj做比较,在Object类中的equals方法,本身和 “ == ”符号相同,都是比较对象的内存地址,建议每个类都应该重写equals方法,不要比较内存地址,而去比较我们关心的数据,因为我们关心的是内容数据,而不是内存地址 基本类型对比的是内容,引用类型比较的是内存地址
-
**String toString():**表示把一个对象转换为字符串.打印对象时,其实打印的就是对象的toString方法
打印的是对象的十六进制的hashCode值,但是我们更关心对象中存储的数据. 建议重写toString方法
包装类
在面向对象中,”一切皆对象” 那么
//age代码哪里有对象??? 这只是基本类型变量,不是对象
int age=17;
基本数据类型缺少对象,如果要有对象,那么必须得有类
八大基本数据类型的包装类都使用final修饰,都是最终类,都不能被继承.
基本数据类型 | 包装类 |
---|---|
byte | Byte |
short | Short |
int | Integer |
long | Long |
char | Character |
float | Float |
double | Double |
boolean | Boolean |
**装箱:**把基本类型数据转成对应的包装类对象。
**拆箱:**把包装类对象转成对应的基本数据类型数据。
Java8是自动进行装箱和拆箱的 底层依然是装箱和拆箱操作,只是简化开发
//装箱
//方式1
Integer num1=new Integer(17);
//方式2
Integer num2=Integer.valueOf(17);
//拆箱
int num3=num1.intValue();
//自动装箱 直接将基本类型赋值给包装类对象
Integer num4=17;
//自动拆箱: 直接将包装类对象赋给对应的基本类型
int num5=num4;
基本类型和包装类型之间的转化
- 装箱:
Integer i1 = new Integer(123);
Integer i2 = Integer.valueOf(123);//推荐,带有缓存.
-
拆箱:
int val = i1.intValue();
String类型和基本类型/包装类型之间的转化
-
把String转换为包装类类型:
//方式1 Integer i1 = Integer.valueOf(“123”); //方式2 Integer i2= new Integer(“123”);
-
把包装类对象转换为String.
String str = 任何对象.toString();
-
把基本数据类型转换为String
String str=123+"";
-
把String转换为基本数据类型:
// static xxx parseXxx(String s) :xxx表示8大基本数据类型 String input=”12345”; int num = Integer.parseInt(input);
包装类和基本数据类型之间的区别 (以int和Integer举例)
-
默认值
int的默认值是0.
Integer的默认值为null. -
包装类中提供了该类型相关的很多算法操作方法
static String toBinaryString(int i) :把十进制转换为二进制
static String toOctalString(int i) : :把十进制转换为八进制
static String toHexString(int i) : :把十进制转换为十六进制 -
在集合框架中,只能存储对象类型,不能存储基本数据类型值
Integer和int不是相同的数据类型
String
String对象的创建:
1):直接赋一个字面量: String str1 = “ABCD”;
2):通过构造器创建: String str2 = new String(“ABCD”);
两种方式有什么区别,分别在内存中如何分布?
字符串的比较操作:
- 使用”==”号: 只能比较引用的内存地址是否相同.
- 使用equals方法: String类重写了Object的equals方法,比较的是字符内容.
常量池:专门存储常量的地方,都指的方法区中
String str1 = “ABCD”; 最多创建一个String对象,最少不创建String对象.
如果常量池中,以及存在”ABCD”,那么str1直接引用,此时不创建String对象.
否则,先在常量池先创建”ABCD”内存空间,再引用.
String str2 = new String(“ABCD”);
最多创建两个String对象,至少创建一个String对象.
new关键字:绝对会在堆空间,创建内存区域. 常量池存在ABCD则指向它所以至少创建一个String个对象.
String对象比较:
- 单独使用" "**引号创建**的字符串都是直接量,编译期就已经确定存储到常量池中
- 使用**new String("")创建**的对象会存储到堆内存中,是运行期才创建;
- 使用只包含直接量的字符串连接符如**“aa” + "bb"创建**的也是直接量编译期就能确定,已经确定存储到常量池中;
- 使用包含String直接量(无final修饰符)的字符串表达式(如"aa" + s1)创建的对象是运行期才创建的,存储在堆中;
StringBuffer和StringBuilder
String和StringBuilder以及StringBuffer的区别(建议使用StringBuilder):
StringBuffer和StringBuilder都表示可变的字符串,功能方法都是相同的.
唯一的区别:
StringBuffer: StringBuffer中的方法都使用了synchronized修饰符,表示同步的,在多线程并发的时候可以保证线程安全,保证线程安全 的时候,性能(速度)较低.
StringBuilder:StringBuilder中的方法都没有使用了synchronized修饰符,不安全,但是性能较高.
StringBuffer的构造方法:
- public StringBuffer():无参构造方法
- public StringBuffer(String str):指定字符串内容的字符串缓冲区对象
StringBuffer的方法:
- public int length():返回长度(字符数)
StringBuffer的添加功能:
- public StringBuffer append(String str):可以把任意类型数据添加到字符串缓冲区里面,并返回字符串缓冲区本身
更多方法详情可以点击在线文档
文档
在线文档地址 https://docs.oracle.com/javase/8/docs/api/index.html
-
文档的创建和转换
byte[] getBytes():把字符串转换为byte数组
char[] toCharArray():把字符串转换为char数组
String(byte[] bytes):把byte数组转换为字符串
String(char[] value):把char数组转换为字符串 -
获取字符串信息
int length() 返回此字符串的长度
char charAt(int index) 返回指定索引处的 char 值
int indexOf(String str)返回指定子字符串在此字符串中第一次出现处的索引。
int lastIndexOf(String str)返回指定子字符串在此字符串中最右边出现处的索引 -
字符串比较判断
boolean equals(Object anObject) 将此字符串与指定的对象比较。
boolean equalsIgnoreCase(String anotherString) 将此 String 与另一个 String 比较,不考虑大小写
boolean contentEquals(CharSequence cs) 将此字符串与指定的 CharSequence 比较 -
字符串大小写转换
String toUpperCase() 把当前字符串转换为大写(谁调用该方法,谁就是当前字符)
String toLowerCase() 把当前字符串转换为小写
集合框架
集合框架是为表示和操作集合而规定的一种统一的标准的体系结构。
三大块内容:对外的接口、接口的实现和对集合运算的算法(底层都对应着某一种数据结构的算法)。
为什么需要集合框架? 专注于业务开发,而不是数据结构和算法.
常用的集合类:
-
Set(集):集合中的对象不按特定方式排序,不允许元素重复.
-
List(列表):集合中的对象按照索引位置排序,允许元素重复.
-
Map(映射):集合中每一个元素都包含一对key和value对象.不允许key对象重复,值对象可以重复.
集合类的操作方法:
-
增加:
boolean add(Object e) 将指定元素添加到此向量的末尾,等价于addElement方法。
void add(int index, Object element) 在此向量的指定位置插入指定的元素。
boolean addAll(Collection c) :把c集合中的元素添加到当前集合对象中. -
删除:
Object remove(int index) :删除指定索引位置的元素,并返回删除之后的元素.
boolean remove(Object o):删除指定的元素.
boolean removeAll(Collection c):从此集合中移除包含在指定 集合c中的所有元素。
boolean retainAll(Collection c):在此集合中仅保留包含在指定 集合c中的元素,求两个集合的交集 -
查询:
int size() :返回当前集合中存储几个元素.
boolean isEmpty():判断当前集合中元素个数是否为0.
Object get(int index):查询指定索引位置的元素.
Object[] toArray():把集合对象转换为Object数组. -
修改:
Object set(int index, Object element) :修改当前集合中指定索引位置的元素.返回被替换的旧的元素.
更多方法详情可以点击在线文档
集合迭代操作
把集合做的元素一个一个的遍历取出来.
迭代器对象:
Iterator: 迭代器对象,只能从上往下迭代.
boolean hasNext(); 判断当前指针后是否有下一个元素
Object next():获取指针的下一个元素,并且移动指针.
-
查询:
int size() :返回当前集合中存储几个元素.
boolean isEmpty():判断当前集合中元素个数是否为0.
Object get(int index):查询指定索引位置的元素.
Object[] toArray():把集合对象转换为Object数组. -
修改:
Object set(int index, Object element) :修改当前集合中指定索引位置的元素.返回被替换的旧的元素.
更多方法详情可以点击在线文档
集合迭代操作
把集合做的元素一个一个的遍历取出来.
迭代器对象:
Iterator: 迭代器对象,只能从上往下迭代.
boolean hasNext(); 判断当前指针后是否有下一个元素
Object next():获取指针的下一个元素,并且移动指针.