# 面向对象
# 对象
1、对象是啥?如何得到
对象就是一种特殊的数据结构。对象是用类 new 出来的,有了类就可以创建出对象
public class 类名{ | |
1、变量,用来说明对象可以处理什么数据 | |
2、方法,描述对象有什么功能 | |
...... | |
} |
# 构造器
构造器是什么样子的?
public class Student{ | |
public Student() { | |
.... | |
} | |
} |
构造器有什么特点?
创建对象时,对象回去调用构造器
Student s = new Student(); |
构造器的常见应用场景
创建对象时,同时完成对对象成员变量的初始化赋值
构造器的注意事项
1、类默认自带一个无参构造器
2、如果为类定义了一个有参数构造器,类默认的无参构造器就没有了,此时如果还想使用无参构造器,就必须手写一个无参构造器出来。
# this 关键字
this 就是一个变量,可以用在方法中,来拿到当前对象
那个对象调用方法,this 就指向哪一个对象,也就是拿到哪一个对象
this 可以用来解决对象的成员变量与方法内部变量的名称一样时,导致访问冲突问题的
# 封装
封装就是用类设计对象处理某一个事物的数据时,应该要把处理的数据,以及处理这些数据的方法设计到一个对象中去。
面向对象的三大特征:封装,继承,多态
封装的设计规范:合理隐藏,合理暴露
公开成员,可以使用 public 修饰
隐藏成员,可以使用 private 修饰
# 实体类
实体类的成员变量必须私有,且要为他们提供 get,set 方法;必须有无参构造器
仅仅只是一个用来保存数据的 java 类,可以用它创建对象,保存某个事物的数据
实体类的应用场景:实体类对应的是软件开发里比较流行的开发方式 数据和数据的业务处理相分离
# static
# 变量
叫静态,可以修饰成员变量、成员方法
static 修饰的成员变量叫类变量(静态成员变量),类名。静态变量(推荐),对象名。静态变量(不推荐)
无 static 修饰的成员变量叫实例变量(对象变量),属于对象,每个对象中都有一份。
静态变量:数据只需要一份,且需要被共享时(访问,修改)
实例变量:每个对象都要有一份,数据各不同
访问自己类中的类变量,可以省略类名不写,在某个类中访问其他类的类变量,必须带类名访问。
# 方法
static 修饰的成员方法叫静态方法,属于类,可以直接用类名访问,也可以用对象访问
类名。静态方法(推荐)
对象名。静态方法(不推荐)
无 static 修饰的成员方法叫实例方法(对象方法),属于对象只能用对象访问
静态方法可以用来设计工具类
工具类中的方法都是静态方法,每个类方法都是用来完成一个功能
提高了代码的复用性;调用方便,提高了开发效率
如果工具类使用实例方法,实例方法需要创建对象来调用,会浪费内存
工具类不需要创建对象,建议将工具类的构造器私有化
静态方法中可以直接访问静态成员,不可以直接访问实例成员
实例方法中既可以直接访问静态静态成员,也可以直接访问实例成员
实例方法中可以出现 this 关键字,静态方法中不可以出现 this 关键字
# 继承
# 继承
java 中提供一个关键字 extends,用这个关键字可以让一个类与另一个类建立起父子关系
public class B extends A{ | |
} |
子类能够继承父类的非私有成员(成员变量,成员方法)
子类的对象是由子类和父类共同完成的
# 权限修饰符
用来限制类中的成员(成员变量,成员方法, 构造器)能够被访问的范围
private:只能本类
缺省:本类,同一个包中的类
protected:本类,同一个包中的类,子孙类中
public:任意位置
修饰符 本类 同一个包中的类 子孙类 任意类
private 1 0 0 0
缺省 1 1 0 0
protected 1 1 1 0
public 1 1 1 1
# 继承的特点
在子类方法中访问其他成员(成员变量,成员方法),是依照就近原则的
先在子类局部范围内找,然后子类成员范围内找,然后父类成员范围内找,如果父类范围还没有找到则报错。
如果出现重名的成员,会优先使用子类,可以通过 ==super.== 关键字指定访问父类成员。
java 中类是单继承的,一个类只能继承一个直接父类。
object 是所有类的祖宗类。
# 方法重写
当子类觉得父类中的某个方法不好用,或者无法满足自己的需求时,子类可以重写一个方法名称,参数列表一样的方法,去覆盖父类的这个方法。
子类重写父类方法时,访问权限必须大于或等于父类该方法的权限
重写的方法返回值类型,必须与被重写方法的返回类型一样,或者范围更小
私有方法、静态方法 不能被重写,如果重写会报错。
# 子类构造器
子类的全部构造器都会先调用父类的构造器,再执行自己
默认情况下,子类全部构造器的第一行代码都是 super (),他会调用父类的无参数构造器。
如果父类没有无参构造器,则我们必须在子类构造器的第一行手写 super,指定去调用父类的有参数构造器。
class Teacher extends People{ | |
a | |
public Teacher(a, b, c){ | |
super(b, c); | |
this.a = a | |
} | |
} |
# this () 调用兄弟构造器
在任意类的构造器中,是可以通过 this (...) 去调用该类的其他构造器的。
public class Student{ | |
private String schoolName; | |
private String name; | |
public Student(String name){ | |
this(name, "hhh"); | |
} | |
public Student(String name, String schoolName){ | |
this.name = name; | |
this.schoolName = schoolName; | |
} | |
} |
this (...), super (...) 都只能放在构造器的第一行,因此,有了 this (...) 就不能写 super (...) 了,反之亦然
# 多态
# 认识多态
多态是在继承 / 实现情况下的一种现象,表现为:对象多态,行为多态
People p1 = new Student(); | |
p1.run(); | |
People p2 = new Teacher(); | |
p2.run(); |
多态的前提:有继承 / 实现关系;存在父类引用子类对象;存在方法重写
多态是对象,行为的多态,java 中的 == 属性(成员变量)== 不谈多态。
# 多态的好处
在多态形式下,右边对象是解耦合的,更便于扩展和维护
People p1 = new Student(); | |
p1.run(); |
定义方法时,使用父类类型的形参,可以接收一切子类对象,扩展性更强,更便利。
多态下不能使用子类的独有功能
# 多态下的类型转换
自动类型转换:父类 变量名 = new 子类 ();
People p = new Teacher(); |
强制类型转换:子类 变量名 = (子类) 父类变量
Teacher t = (Teacher)p; |
存在继承 / 实现关系就可以在编译阶段进行强制类型转换,编译阶段不会报错。
运行时,如果发现对象的真实类型与强制转换后的类型不同,就会报类型准换异常 (ClassCastException) 的错误出来
People p = new Teacher(); | |
Student s = (Student) p; // java.lang.ClassCastException |
强转前,java 建议:使用 instanceof 关键字,判断当前对象的真实类型,再强制转换。
p instanceof Student |
# final
# 认识 final
final 关键字是最终,可以修饰:类、方法、变量
修饰类:该类被称为最终类,特点是不能被继承了
修饰方法:该方法被称为最终方法,特点是不能被重写了
修饰变量:该变量有且仅有被赋值一次。
final 修饰基本类型的变量,变量存储的数据不能被改变
final 修饰的引用类型的变量,变量存储的地址 不能被改变,但地址指向的对象内容可以改变
static final 修饰的成员变量被称为常量
作用:常用于记录系统的配置信息
public class Constant{ | |
public static final String SCHOOL_NAME = "船只教育"; | |
} |
变量名的命名规范:建议用大写英文单词,多个单词间使用下划线连接起来
使用常量记录系统配置信息的优势:代码可读性好,可维护性也好。
程序编译后,常量会被” 宏替换 “:出现常量的地方全部会被替换成其字面量。可以保证使用常量和直接使用字面量的性能是一样的。
# 单例类(设计模式)
具体问题的最优解决方案,确保一个类只能创建一个对象。
单例:把类的构造器私有,定义一个静态变量存储类的一个对象,提供一个静态方法返回对象。
在任务管理器对象时,获取运行时对象。
饿汉式单例:拿对象前,对象早就创建好了
public class A{ | |
private static A a = new A(); | |
private A(){ | |
} | |
public static A getObject(){ | |
return a; | |
} | |
} |
懒汉式单例:拿对象时,才开始创建对象。
public class B{ | |
private static B b; | |
private B(){ | |
} | |
public static B getObject(){ | |
if(b == null){ | |
b = new B(); | |
} | |
return b; | |
} | |
} |
# 枚举类
public enum A{ | |
X, Y, Z; | |
} |
枚举类都是最终类,不可以被继承,枚举类都是继承 java.lang.Enum 类的
枚举类的第一行只能罗列一些名称,这些名称都是常量,并且每个常量会记住枚举类的一个对象
枚举类的构造器都是私有的,因此,枚举类对外不能创建对象。
编译器对枚举类新增了几个方法。
# 枚举类的常见应用场景
枚举类适合做信息分类和标致
# 抽象类
关键字:abstract,可以用来修饰类,成员方法
abstract 修饰类,这个类就是抽象类
abstract 修饰方法,这个方法就是抽象方法
public abstract class 类名{ | |
public abstract 返回值 方法名(); | |
} |
抽象类中不一定要有抽象方法,有抽象方法的类必须是抽象类
类有的成员:成员变量,方法,构造器,抽象类都可以有
抽象类的主要特点:抽象类不能创建对象,仅作为一种特殊的父类,让子类继承并实现
一个类继承抽象类,必须重写完抽象类的全部抽象方法,否则这个类也必须定义成抽象类。
# 抽象类的好处
父类知道每个子类要做的某个行为,但每个子类要做的情况不一样,父类就定义成抽象方法,交给子类去重写实现,设计这样的抽象类,就是为了更好的支持多态。
# 模板方法
提供一个方法作为完成某类功能的模板,模板方法封装了每个实现步骤,但允许子类提供特定步骤的实现
模板方法设计某事可以:提高代码的复用,并简化子类设计
写法:
1、定义一个抽象类
2、在里面定义 2 个方法
一个是模板方法,把共同的实现步骤放里面
一个是抽象方法:不确定的步骤,交给具体的子类实现
模板方法是给子类直接使用的,不能被子类重写,建议使用 final 关键字修饰模板方法
一旦子类重写了模板方法,模板方法就失效了。
# 接口
# 接口概述
java 提供了一个关键字 interface 定义出接口
public interface 接口名{ | |
// 成员变量(常量) | |
// 成员方法(抽象方法) | |
} |
注意:接口不能创建对象
接口是用来被实现的,实现接口的类型称为实现类,一个类可以实现多个接口
修饰符 class 实现类名 implements 接口1、接口2、接口3.....{ | |
// 实现类实现多个接口,必须重写完全部接口的全部抽象方法,否则实现类需要定义为抽象类 | |
} |
# 接口好处
弥补了类单继承的不足,一个类同时可以实现多个接口,使类的角色更多,功能更强大
让程序可以面向接口编程,更利于程序的解耦合。
# 接口新增的三种方法
public interface A{ | |
// 默认方法(实例方法):使用 default 修饰,默认会被加 public 修饰 | |
// 只能使用接口的实现类对象调用 | |
default void test1(){ | |
} | |
// 私有方法:必须用 private 修饰 | |
// 接口内部调用 | |
private void test2(){ | |
} | |
// 类方法,使用 static 修饰,默认 public | |
// 只能用接口名调用 | |
static void test3(){ | |
} | |
} |
# 接口的注意事项
1、接口与接口可以多继承:一个接口可以继承多个接口
2、一个接口继承多个接口,如果多个接口中存在方法签名冲突、则此时不支持多继承,也不支持多实现。
3、一个类继承了父类,又同时实现了接口,如果父亲中和接口中有同名的默认方法,实现类会先用父类的
4、一个类实现了多个接口,如果多个接口中存在同名的默认方法,可以不冲突,这个类方法重写即可。
# 接口与抽象类
相同点:
1、都是抽象形式,都可以有抽象方法,都不能创建对象。
2、都是派生子类形式,抽象类是被子类继承使用,接口是被实现类实现。
3、一个类继承抽象类,或者实现接口,都必须重写他们的抽象方法,否则自己称为抽象类或者报错。
4、都支持多态,能够实现解耦合
不同点:
1、抽象类中可以定义类的全部普通成员,接口只能定义常量,抽象方法(JDK8 新增的三种方法)
2、抽象类只能被类单继承,接口可以被类多实现。
3、一个类继承抽象类就不能继承其他类,一个接口实现了接口还可以继承其他类或者实现其他接口
4、抽象类体现模板思想,更利于做父类,实现代码的复用
5、接口更适合做功能的解耦合,解耦合性更灵活。
# 类中的成分
# 代码块
代码块是类的 5 大成分之一(成员变量,构造器,方法,代码块,内部类)
代码块分为两种:
静态代码块:
格式:static {}
特点:类加载时自动执行,由于类只会加载一次,所以静态代码块也只会执行一次。
作用:完成类的初始化,例如:对静态变量的初始化赋值。
实例代码块:
格式:{}
特点:每次创建对象时,执行实例代码块,并在构造器前执行。
作用:和构造器一样,都是用来完成对象的初始化,例如:对实例变量进行初始化赋值
# 内部类
成员内部类
就是类中的一个普通成员,类似前面学过的成员变量,成员方法。
public class Outer{ | |
public class Inner{ | |
} | |
} | |
// 创建对象的格式 | |
外部类名.内部类名 对象名 = new 外部类(...).new 内部类(...); | |
Outer.Inner in new Outer().new Inner(); |
成员内部类中访问其他成员的特点:
1、成员内部类中可以直接访问外部类的实例成员,静态成员。
2、成员内部类的实例方法中,可以直接拿到当前外部类对象,格式是:外部类名.this。
静态内部类
有 static 修饰的内部类,属于外部类自己特有
public class Outer{ | |
public static class Inner{ | |
} | |
} | |
// 创建 | |
外部类.内部类名.对象名 = new 外部类.内部类(...); | |
Outer.Inner.in = new Outer.Inner(); |
匿名内部类
是一种特殊的局部内部类
所谓匿名:指的是程序员不需要为这个类声明名字,默认有个隐藏的名字。
new 类或接口(参数值...){ | |
类体(一般是方法重写); | |
}; | |
new Animal(){ | |
@Override | |
public void cry(){ | |
} | |
} |
特点:匿名内部类本质就是一个子类,并会立即创建出一个子类对象。
作用:用于更方便的创建子类对象。
# lambda 表达式
可以用于替代某些匿名内部类对象,从而让程序更简洁,可读性更好。
(被重写方法的形参列表) -> { | |
被重写方法的代码体; | |
} |
注意:lambda 表达式只能替代函数式接口的匿名内部类
函数式接口:有且仅有一个抽象方法的接口,注意大部分函数式接口上面都会有一个 @FunctionalInterface 的注释,用来注解用于约束当前接口必须是函数式接口。
lambda 表达式的省略规则:
1、参数类型全部可以省略不写
2、如果只有一个参数,参数类型省略的同时 () 也可以省略,但多个参数不能省略 ()
3、如果 lambda 表达式中只有一行代码,大括号可以不写,同时要省略分号 ";" 如果这行代码是 return 语句,也必须去掉 return。
# 方法引用
# 静态方法引用
类名::静态方法 |
使用场景:如果某个 Lambda 表达式只有一个静态方法,并且”->“前后参数的形式一致,就可以使用静态方法引用。
# 实例方法引用
对象名::实例方法 |
使用场景:如果某个 Lambda 表达式里只是通过对象名称调用一个实例方法,并且 "->" 前后参数的形式一致,就可以使用实例方法引用。
# 特定类的方法应用
特定类的名称::方法 |
使用场景:如果某个 Lambda 表达式只是调用一个特定类型的实例方法,并且前面参数列表中的第一个参数是作为方法的主调,后面的所有参数都是作为该实例方法的入参的,则此时就可以使用特定类型的方法引用。
# 构造器引用
类名::new |
使用场景:如果某个 Lambda 表达式里只是在创建对象,并且”->“前后参数情况一致,就可以使用构造器引用。
# 常用 API
# String 创建字符串对象的方式
- 方式一: Java 程序中的所有字符串文字 (例如 "abc") 都为此类的对象.
String name = ""; |
- 方式二:调用 String 类的构造器初始化字符串对象
# String 提供的常用方法
// 返回字符串的长度 | |
public int length() | |
// 获取某个索引的字符并返回 | |
public char charAt(int index) | |
// 将当前字符串转换成字符数组返回 | |
public char[] toCharArray() | |
// 判断当前字符串与另一个字符串的内容一样,一样返回 true | |
public boolean equals(Object anObject) | |
// 判断当前字符串与另一个字符串的内容一样,一样返回 true (忽略大小写) | |
public boolean equalsIgnoreCase(String anotherString) | |
// 根据开始和结束索引来进行截取,得到新的字符串返回 | |
public String substring(int beginIndex) | |
// 使用新的值, 将字符串中的旧值替换,得到新的字符串 | |
public String replace(CharSequence target, CharSequence replacement) | |
// 判断字符串中是否包含了某个字符串 | |
public boolean contains(CharSequence s) | |
// 判断字符串是否以某个字符串内容开头,开头返回 true,反之 | |
public boolean startsWith(String prefik) | |
// 把字符串按照某个字符串内容进行分割,并返回字符串数组 | |
public String[] split(String regex) |
# ArrayList 集合
常用方法:
// 创建一个空的集合对象 | |
public ArrayList() | |
// 将指定的元素添加到此集合的末尾 | |
public boolean add(E e) | |
// 在此集合中的指定位置插入指定的元素 | |
public void add(int index, E element) | |
// 返回指定元素索引处的元素 | |
public E get(int index) | |
// 返回集合中元素的个数 | |
public int size() | |
// 删除指定索引处的元素,返回被删除的元素 | |
public E remove(int index) | |
// 删除指定的元素,返回删除是否成功 | |
public boolean remove(Object o) | |
// 修改指定索引处的元素,返回被修改的元素 | |
public E set(int index, E element) |