# 面向对象

# 对象

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)

更新于 阅读次数