10.OOP高级
类变量和类方法
类变量: 也叫静态变量/静态属性,是该类的所有对象共享的变量。任何一个该类的对象对方问它时,取到的都是相同的值,同样任何一个该类的对象去修改它时,修改的也是同一个变量。这个从前面的图也可看出来。
语法:
- 访问修饰符 static 数据类型 变量名:
- static 访问修饰符 数据类型 变量名;
用法:
- 类名.类变量名
- 对象名.类变量名
tips:
- 什么时候需要用类变量
当我们需要让某个类的所有对象都共享一个变量时,就可以考虑使用类变量(静态变量):比如:定义学生类,统计所有学生共交多少钱。Student(name,static fee) - .类变量与实例变量(普通属性)区别
类变量是该类的所有对象共享的,而实例变量是每个对象独享的。 - 加上static称为类变量或静态变量,否则称为实例变量/普通变量/非静态变量
- 类变量可以通过类名.类变量名或者对象名.类变量名来访问,但java设计者推荐我们使用类名.类变量名方式访问。【前提是满足访问修饰符的访问权限和范围】
- 实例变量不能通过类名.类变量名方式访问。
- 类变量是在类加载时就初始化了,也就是说,即使你没有创建对象,只要类加载了,就可以使用类变量了。
- 类变量的生命周期是随类的加载开始,随着类消亡而销毁。
类方法
类方法也叫静态方法。
形式如下:
访问修饰符 static 数据返回类型 方法名(){ }
static 访问修饰符 数据返回类型 方法名(){ }
应用:>当方法中不涉及到任何和对象相关的成员,则可以将方法设计成静态方法,提高开发效率。
比如:工具类中的方法utils
|Math类、Arrays类、Collections 集合类看下源码:
小结
在程序员实际开发,往往会将一些通用的方法,设计成静态方法,这样我们不需要创建对象就可以使用了,比如打印一维数组,冒泡排序,完成某个计算任务等.
tips:
1)类方法和晋通方法都是随看类的加载而加载,将结构信息存储在方法区:
类方法中无this的参数
普通方法中隐含着this的参数
2)类方法可以通过类名调用,也可以通过对象名调用。
3)普通方法和对象有关,需要通过对象名调用,比如对象名.方法名(参数),不能通过类名调用。
4)类方法中不允许使用和对象有关的关键字,比如this和super。普通方法(成员方法)可以。
5)类方法(静态方法)中只能访问静态变量或静态方法。
6)普通成员方法,既可以访问非静态成员,也可以访问静态成员。
小结:静态方法,只能访问静态的成员(成员包括方法和变量);非静态的方法,可以访问静态成员和非静态成员(必须遵守访问权限)
代码块
代码化块又称为初始化块,属于类中的成员[即是类的一部分],类似于方法,将逻辑语句封装在方法体中,通 { }包围起来。
但和方法不同,没有方法名,没有返回,没有参数,只有方法体,而且不用通过对象或类显式调用,而是加载类时,或创建对象时隐式调用。
代码块的好处:
代码块细节:
单列设计模式
什么时单列设计模式?
饿汉式 和 懒汉式 单列设计模式的实现
1)构造器私有化 =》 防止直接 new
2)类的内部创建对象
3)向外暴露一个静态的公共方法
4)代码实现
饿汉式
public class SingTon1 {
public static void main(String[] args) {
GirlFriend instance = GirlFriend.getInstance();
System.out.println(instance);
GirlFriend instance2 = GirlFriend.getInstance();
System.out.println(instance2);
System.out.println(instance == instance2);//T
}
}
//只有一个类 GirlFriend
class GirlFriend {
private String name;
private static GirlFriend gf = new GirlFriend("小红");
//如何保障我们只能创建一个 GirlFriend 对象
// 步骤[单例模式-饿汉式]
// 1. 将构造器私有化
// 2. 在类的内部直接创建对象(该对象是 static)
// 3. 提供一个公共的 static 方法,返回 gf 对象
private GirlFriend(String name) {
this.name = name;
}
public static GirlFriend getInstance() {
return gf;
}
}
懒汉式
public class SingTon2 {
public static void main(String[] args) {
Cat instance = Cat.getInstance();
System.out.println(instance);
Cat instance2 = Cat.getInstance();
System.out.println(instance2);
System.out.println(instance == instance2);//T
}
}
//使用单列设计模式
class Cat{
private String name;
public static int n1=999;
private static Cat cat;//默认是 null
//步骤:
//1.仍然是构造器私有化
//2.定义一个static静态属性对象
//3.提供一个public的static方法,可以返回一个Cat对象
//4.懒汉式:只有当用户使用getInstance时,才返回cat对象,
//后面再次调用是,会返回上次调用的对象,从而保证了单列
private Cat(String name) {
this.name = name;
}
public static Cat getInstance(){
if (cat == null){
cat =new Cat("小可爱");
}
return cat;
}
}
饿汉式VS懒汉式
final 关键字
细节:
抽象类
当父类的一些方法不确定是,可以用abstract关键字来修饰该方法,这个方法就是抽象方法,用abstract来修饰该类就是抽象类。
抽象类介绍
1)用abstract 关键字来修饰一个类时,这个类就叫抽象类
访问修饰符 abstract 类名{
}
2)用abstract关键字来修饰一个方法时,这个方法就是抽象方法
访问修饰符 abstract 返回类型 方法名(参数列表);//没有方法体
3)抽象类的价值更多作用是在于设计,是设计者设计好后,让子类继承并实现抽象类()
Tips:
-
抽象类不能被实例化
-
抽象类不一定包含abstract方法
-
一旦类包含了abstract方法,则这个类必须声明为abstract
-
abstract只能修饰类和方法,不能修饰属性和其他的。
-
抽象类可以有任意成员【抽象类本质还是类】比如:非抽象方法、构造器、静态属性等等
-
抽象方法不能有主体,即不能实现
-
如果一个类继承了抽象类,则他必须实现抽象类的所有抽象方法,除非他自己也声明为abstract类
-
抽象方法不能使用private、final、static来修饰,因为这些关键字都是和重写相违背的
接口
Tips:
小结: 当子类继承了父类,就自动的拥有父类的功能
如果子类需要扩展功能,可以通过实现接口的方式扩展.
可以理解 实现接口 是 对 java 单继承机制的一种补充
内部类
如果定义类在局部位置(方法中/代码块):
(1)局部内部类(2)匿名内部类
定义在成员位置
(1)成员内部类 (2)静态内部类
一个类的内部又完成的嵌套了另一个类。被嵌套的类称为内部类(inner class),嵌套其他类的类称为外部类(outer class)。
类的五大成员:属性、方法、构造器、代码块、内部类
内部类最大的特点就是可以直接访问私有属性,并且可以体现类与类之间的包含关系。
基本语法:
内部类的分类:
局部内部类的使用:
匿名内部类的使用【重要!!】
成员内部类的使用:
静态内部类的使用:
11.枚举和注解
枚举(enum)
枚举是一组常量的集合。
枚举实现的二种方式
1)自定义类实现枚举
2)使用enum 关键字实现枚举
自定义类实现枚举
enum关键字实现枚举(class 前加上 enum)
1)使用enum关键字开发一个枚举类时,默认会继承Enum类,而且时一个final类
`2)传统的 public static final Season2 SPRING = new Season2(“春天”,”温暖“);
简化成SPRING(“春天”,“温暖”);
3)如果使用无参构造器 创建 枚举对象 ,则实参列表和小括号都可以省略
4)当有多个枚举对象是,使用 ,间隔,最后有一个分号结尾
5)枚举对象必须放在枚举类的首行
enum常用方法说明
说明:使用关键字 enum时,会隐式继承Enum类。
- toString:Enum 类已经重写过了,返回的是当前对象 名,子类可以重写该方法,用于返回对象的属性信息
- name:返回当前对象名(常量名),子类中不能重写
- ordinal:返回当前对象的位置号,默认从 0 开始
- values:返回当前枚举类中所有的常量
- valueOf:将字符串转换成枚举对象,要求字符串必须 为已有的常量名,否则报异常!
- compareTo:比较两个枚举常量,比较的就是编号!
enum实现接口
1)使用enum关键字后,就不能在继承其他类了,因为 enum 会隐式继承 Enum,而 Java 是单继承机制。
2)枚举类和普通类一样,可以实现接口,如下形式。
enum 类名 implements 接口 1,接口 2 { }
注解
- 注解(Annotation)也被称为元数据(Metadata),用于修饰解释 包、类、方法、属性、构造器、局部变量等数据信息。
- 和注释一样,注解不影响程序逻辑,但注解可以被编译或运行,相当于嵌入在代码中的补充信息。
- 在 JavaSE 中,注解的使用目的比较简单,例如标记过时的功能,忽略警告等。在 JavaEE 中注解占据了更重要的角 色,例如用来配置应用程序的任何切面,代替 java EE 旧版中所遗留的繁冗代码和 XML 配置等
基本的Annotation 介绍
使用 Annotation 时要在其前面增加 @ 符号, 并把该 Annotation 当成一个修饰符使用。用于修饰它支持的程序元 素
三个基本的 Annotation:
- @Override: 限定某个方法,是重写父类方法, 该注解只能用于方法
- @Deprecated: 用于表示某个程序元素(类, 方法等)已过时
- @SuppressWarnings: 抑制编译器警告
元注解的种类(了解)
- Retention //指定注解的作用范围,三种 SOURCE,CLASS,RUNTIME
- Target // 指定注解可以在哪些地方使用
- Documented //指定该注解是否会在 javadoc 体现 4) Inherited //子类会继承父类注解
12. 异常 Exception
异常类介绍:
异常体系图
小结:
运行异常
常见的运行时异常
- NullPointerException 空指针异常
- ArithmeticException 数学运算异常
- ArrayIndexOutOfBoundsException 数组下标越界异常
- ClassCastException 类型转换异常
- NumberFormatException 数字格式不正确异常[ ]
编译异常
编译异常是指在编译旗舰,就必须处理的异常,否则代码不能通过编译。
常见的编译异常
异常处理
异常处理就是当异常发生时,对异常处理的方式。
异常处理的方式:
1)示意图:
2)示意图:
try-catch 异常处理
注意事项:
try-catch-finally 小结:
throws 异常处理
Tips:
自定义异常类
概念:
步骤:
package com.hspedu.customexception_;
public class CustomException {
public static void main(String[] args) /*throws AgeException*/ {
int age = 180;
//要求范围在 18 – 120 之间,否则抛出一个自定义异常
if(!(age >= 18 && age <= 120)) {
//这里我们可以通过构造器,设置信息
throw new AgeException("年龄需要在 18~120 之间");
}
System.out.println("你的年龄范围正确.");
}
}//自定义一个异常
//1. 一般情况下,我们自定义异常是继承 RuntimeException
//2. 即把自定义异常做成 运行时异常,好处时,我们可以使用默认的处理机制
//3. 即比较方便
class AgeException extends RuntimeException {
public AgeException(String message) {//构造器
super(message);
}
}
throw 和 throws 的区别
13.常用类
包装类
包装类的分类
- 针对八种基本数据类型相应的引用类型—包装类
- 有了类的特点,就可以调用类中的方法。
- 如图:
包装类和基本数据的转换
public class wrapper {
public static void main(String[] args) {
//jdk 5 以前时手动装箱和拆箱
//手动装箱 int ——> Integer
int n1 = 100;
Integer integer = new Integer(n1);
Integer integer1 = Integer.valueOf(n1);
//手动拆箱 Integer ——> int
int i = integer.intValue();
//jdk5 以后自动装箱和自动拆箱
int n2 = 200;
//自动装箱 int ——> Integer
Integer integer2 = n2;//底层使用的是 Integer.valueOf(n2)
//自动拆箱 Integer->int
int n3 = integer2; //底层仍然使用的是 intValue()方法
}
}
包装类型和 String 类型的相互转换
//以 Integer 和 String 转换为例,其它类似:
public class WrapperVSString {
public static void main(String[] args) {
//包装类(Integer)——>String
Integer i=100;//自动装箱
//方式1
String str =i +"";
//方式2
String str2=i.toString();
//方式3
String str3 = String.valueOf(i);
//String——>包装类(Integer)
String str4 ="12345";
Integer i2 = Integer.parseInt(str4);
Integer i3 = new Integer(str4);
}
}
Intege 类面试题总结
//1. 如果 i 在 IntegerCache.low(-128)~IntegerCache.high(127),就直接从数组返回
//2. 如果不在 -128~127,就直接 new Integer(i)
public class WrapperExercise03 {
public static void main(String[] args) {
//示例一
Integer i1 = new Integer(127);
Integer i2 = new Integer(127);
System.out.println(i1 == i2);//F
//示例二
Integer i3 = new Integer(128);
Integer i4 = new Integer(128);
System.out.println(i3 == i4);//F
// 示例三
Integer i5 = 127;//底层 Integer.valueOf(127)
Integer i6 = 127;//-128~127
System.out.println(i5 == i6); //T
// 示例四
Integer i7 = 128;
Integer i8 = 128;
System.out.println(i7 == i8);//F
// 示例五
Integer i9 = 127;
Integer.valueOf(127);
Integer i10 = new Integer(127);
System.out.println(i9 == i10);//F
// 示例六
Integer i11 = 127;
int i12 = 127;
//只有有基本数据类型,判断的是
// 值是否相同
System.out.println(i11 == i12); //T
// 示例七
Integer i13 = 128;
int i14 = 128;
System.out.println(i13 == i14);//T
}
}
String 类
1.String 对象用于保存字符串,也就是一组字符序列
2. “jack” 字符串常量, 双引号括起的字符序列
3. 字符串的字符使用 Unicode 字符编码,一个字符(不区分字母还 是汉字)占两个字节
4. String 类有很多构造器,构造器的重载
// 常用的有 String s1 = new String();
// //String s2 = new String(String original);
//String s3 = new String(char[] a);
//String s4 = new String(char[] a,int startIndex,int count)
//String s5 = new String(byte[] b)
5. String 类实现了接口 Serializable【String 可以串行化:可以在网络传输】
// 实现了接口 Comparable [String 对象可以比较大小]
6. String 是 final 类,不能被其他的类继承
7. String 有属性 private final char value[ ]; 用于存放字符串内容
8. 一定要注意:value 是一个 final 类型, 不可以修改:即 value 不能指向新的地址,但是单个字符内容是可以变化。
创建 String 对象的两种方式
两种方式的区别:
String a =" hello ";//创建a对象
String b= " abc ";//创建b对象
String c= a+b; 共创建了几个对象? 3个
小结: StringBuilder sb=new StringBuilder();sb.append(a);
sb.append(b);sb是在堆中,并且append是在原来字符串的基础上追加的。
规则: String c1=“ab”+“cd”;常量相加,看的是池。
String c1 = a+b;变量相加,是在堆中。
String 类的常见方法一览
StringBuffer 类
1.StringBuffer 的直接父类 是 AbstractStringBuilder
2. StringBuffer 实现了 Serializable, 即 StringBuffer 的对象可以串行化
3. 在父类中 AbstractStringBuilder 有属性 char[] value,不是 final // 该 value 数组存放 字符串内容,引出存放在堆中的
4. StringBuffer 是一个 final 类,不能被继承
5. 因为 StringBuffer 字符内容是存在 char[] value, 所有在变化(增加/删除) // 不用每次都更换地址(即不是每次创建新对象), 所以效率高于 String
String VS StringBuffer
String 和 StringBuffer 相互转换
public class StringAndStringBuffer {
public static void main(String[] args) {
//看 String——>StringBuffer
String str = "hello tom";
//方式1 使用构造器
//返回的才是StringBuffer对象,对str本身没有影响
StringBuffer stringBuffer = new StringBuffer(str);
//方式2 使用append 方法
StringBuffer stringBuffer1 = new StringBuffer();
stringBuffer1 = stringBuffer1.append(str);
//看看 StringBuffer ->String
StringBuffer stringBuffer2 = new StringBuffer("tyy学java");
//方式1 使用StringBuffer提供的toString方法
String s = stringBuffer2.toString();
//方式2 使用构造器
String s1 = new String(stringBuffer2);
}
}
StringBuilder 类
- StringBuilder 继承 AbstractStringBuilder 类
- 实现了 Serializable ,说明 StringBuilder 对象是可以串行化(对象可以网络传输,可以保存到文件)
- StringBuilder 是 final 类, 不能被继承
- StringBuilder 对象字符序列仍然是存放在其父类 AbstractStringBuilder 的 char[] value; // 因此,字符序列是堆中
- StringBuilder 的方法,没有做互斥的处理,即没有 synchronized 关键字,因此在单线程的情况下使用 StringBuilder
小结:
String、StringBuffer 和 StringBuilder 的选择
Arrays 类
System类
BigInteger 和 BigDecimal 类
日期类
1)第一代日期类
2)第二代日期类
3)第三代日期类
public class Date2 {
public static void main(String[] args) {
//使用now()返回表示当前日期时间的 对象
LocalDateTime now = LocalDateTime.now();
System.out.println(now);
//使用DateTimeFormatter 对象来进行格式话
DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss E");
String format = dateTimeFormatter.format(now);
System.out.println("格式化日期:" + format);
}
}
DateTimeFormatter 格式日期类
Instant 时间戳
public class Date3 {
public static void main(String[] args) {
//1.通过 静态方法 now() 获取表示当前时间戳的对象
Instant now = Instant.now();
System.out.println(now);
//2. 通过 from 可以把 Instant 转成 Date
Date date = Date.from(now);
System.out.println(date);
//3. 通过 date 的 toInstant() 可以把 date 转成 Instant 对象
Instant instant = date.toInstant();
System.out.println(instant);
}
}
14.集合
数组
集合
1)集合主要是两组(单列集合,双列集合)
2)Collection 接口有两个重要的子接口 List, Set , 他们的实现子类都是单列集合
3)Map 接口的实现子类 是双列集合,存放的 K-V
集合框架体系
Collection 接口和常 用方法
Collection 接口实现类的特点
add:添加单个元素
remove:删除指定元素
list.remove(0);删除第一个元素
list.remove(true);//指定删除某个元素
list.contains(“jack”);查找元素是否存在
list.size();获取元素个数
list.isEmpty();:判断是否为空
list.clear();清空
ArrayList list2 = new ArrayList();
list2.add(“红楼梦”);
list2.add(“三国演义”);
list.addAll(list2);
addAll:添加多个元素
removeAll:删除多个元素
Collection 接口遍历元素方式 1-使用 Iterator(迭代器)
在调用iterator.next( )方法之前必须要调用iterator.hasNext( )进行检测。若不调用,且下一条记录无效,直接调用it.next( )会抛出NoSuchElementException异常。
Collection 接口遍历对象方式 2-for 循环增强
List 接口和常用方法
List 接口的常用方法
1)void add(int index, Object ele):在 index 位置插入 ele 元素
2)boolean addAll(int index, Collection eles):从 index 位置开始将 eles 中的所有元素添加进来
3)Object get(int index):获取指定 index 位置的元素
4)int indexOf(Object obj):返回 obj 在集合中首次出现的位置
5)int lastIndexOf(Object obj):返回 obj 在当前集合中末次出现的位置
6)Object remove(int index):移除指定 index 位置的元素,并返回此元素
7)Object set(int index, Object ele):设置指定 index 位置的元素为 ele , 相当于是替换.
8)List subList(int fromIndex, int toIndex):返回从 fromIndex 到 toIndex 位置的子集合
注意返回的子集合 fromIndex <= subList < toIndex
List 的三种遍历方式
ArrayList 底层结构和源码分析
ArrayList 的注意事项
ArrayList 的底层操作机制源码分析
Vector 底层结构和源码剖析
Vector 的基本介绍
Vector 和 ArrayList 的比较
LinkedList 底层结构
LinkedList 的底层操作机制
ArrayList 和 LinkedList 比较
Set 接口和常用方法
和 List 接口一样, Set 接口也是 Collection 的子接口,因此,常用方法和 Collection 接口一样.
Set 接口的遍历方式
Set 接口实现类-HashSet
HashSet底层是HashMap。HashMap底层是(数组+链表+红黑树)
Set 接口实现类-LinkedHashSet
Map 接口和常用方法
Map map = new HashMap();
//第一组: 先取出 所有的 Key , 通过 Key 取出对应的 Value
Set keyset = map.keySet();
//(1) 增强 for
System.out.println("-----第一种方式-------");
for (Object key : keyset) {
System.out.println(key + "-" + map.get(key));
}
//(2) 迭代器
System.out.println("----第二种方式--------");
Iterator iterator = keyset.iterator();
while (iterator.hasNext()) {
Object key = iterator.next();
System.out.println(key + "-" + map.get(key));
}
//第二组: 把所有的 values 取出
Collection values = map.values();
//这里可以使用所有的 Collections 使用的遍历方法
//(1) 增强 for
System.out.println("---取出所有的 value 增强 for----");
for (Object value : values) {
System.out.println(value);
}
//(2) 迭代器
System.out.println("---取出所有的 value 迭代器----"); Iterator iterator2 = values.iterator();
while (iterator2.hasNext()) {
Object value = iterator2.next();
System.out.println(value);
}
//第三组: 通过 EntrySet 来获取 k-v
Set entrySet = map.entrySet(); //EntrySet<Map.Entry<K,V>>
//(1) 增强
for System.out.println("----使用 EntrySet 的 for 增强(第 3 种)----");
for (Object entry : entrySet) {
//将 entry 转成 Map.Entry
Map.Entry m = (Map.Entry) entry;
System.out.println(m.getKey() + "-" + m.getValue());
}
//(2) 迭代器
System.out.println("----使用 EntrySet 的 迭代器(第 4 种)----");
Iterator iterator3 = entrySet.iterator();
while (iterator3.hasNext()) {
Object entry = iterator3.next();
//System.out.println(next.getClass());
//HashMap$Node -实现-> Map.Entry (getKey,getValue)
//向下转型 Map.Entry
Map.Entry m = (Map.Entry) entry;
System.out.println(m.getKey() + "-" + m.getValue());
}
Map 接口实现类-HashMap
HashMap 小结
HashMap 底层机制及源码剖析
Map 接口实现类-Hashtable
HashTable 的基本介绍
Hashtable 和 HashMap 对比
Map 接口实现类-Properties![在这里插入图片描述](https://img-blog.csdnimg.cn/a4382618ee474e7eb7ab529e650052ae.png)
总结-开发中如何选择集合实现类(记住)![在这里插入图片描述](https://img-blog.csdnimg.cn/130ccd95bf7e4826b8760d982865593f.png)
Collections 工具类
Collections 工具类介绍
排序操作:(均为 static 方法)
查找、替换
15.泛型
泛型的好处
泛型介绍
泛型的作用:可以在类声明时通过一个标识表示类中某个属性的类型,
或者是某个方法的返回值的类型,或者是参数类型
泛型的声明
泛型的实例化
泛型使用的注意事项和细节
自定义泛型
自定义泛型类
自定义泛型接口
自定义泛型方法