面向对象
了解面向对象
定义
面向对象思想是一种程序设计思想,我们在面向对象思想的指引下,使用java语言去设计、开发计算机程序。这里的对象泛指现实中一切事物,每种事物都具备自己的属性和行为。
特点
- 面向对象是一种符合人们的思考习惯的思想。
- 面向对象的出现,就是为了将复杂的问题简单化。
- 面向对象的出现,将程序员从执行者转变为了指挥者。
优点
可以设计出低耦合的系统,使系统更加灵活,更易于维护。
两个关键点
- 类
类是一组属性和行为的集合。可以看成是一类事物的模版,使用事物的属性特征和行为特征来描述该事物。
属性:就是该事物的状态信息。
行为:就是该事物能做什么。
- 对象
对象是一类事物的具体体现。对象是类的一个实例,必然具备该类事物的属性和行为。
类是对象的抽象化,对象是类的实例化。
面向对象的三大特征
封装
定义
封装可以被认为是一个保护屏障,放置该类的代码和数据被其他类随意访问。要访问该类的数据,必须通过指定的方式。适当的封装可以让代码更容易理解和维护,也加强了代码的安全性。
原则
将属性隐藏起来,若要访问某个属性,通过公共方法对齐访问
使用步骤
- 使用private关键字来修饰成员变量。
- 对需要访问的成员变量,提供对应的一对getXxx()方法和setXxx()方法。
关键字
- private关键字:private是一个权限修饰符,代表最小权限,可以用来修饰成员变量和成员方法。被private修饰后的成员变脸和成员方法,只有在本类中才能访问。
- this关键字:this代表所在类的当前对象的引用(地址值),即对象自己的引用。方法被哪个对象调用,方法中的this就代表那个对象。即谁在调用,this就代表谁。
构造方法
java中的构造方法是一种特殊的方法,用来初始化对象。当一个对象被创建时,构造方法用来初始化该对象,给对象的成员变量赋初始值。以下是他的特点。
- 函数名与类名相同
- 不用定义返回值类型
- 没有具体的返回值
继承
定义
就是子类继承父类的属性和行为,使子类对象具有和父类相同的属性和行为。子类可以直接访问父类中的非私有的属性和行为。
优点
- 提高代码的复用性
- 类与类之间产生了关系,是多态的前提。
关键字
- super:代表父类的存储空间标识(可以理解为父类的引用)
- this:代表当前对象的引用(谁调用就代表谁)
特点
- java只支持单继承,不支持多继承
- java支持多层继承(继承体系)
- 最顶层父类是Object类。所有的类都默认继承Object,作为父类
- 子类和父类是一种的相对的概念,父类也可能是另一个父类的子类。(继承体系)
多态
定义
是指同一行为,具有多个不同表现形式。
前提
- 继承或者实现(二选一)
- 方法的重写:不重写,也就没有意义
- 父类引用指向子类对象(格式体现)
体现
父类类型 变量名 = new 子类对象;
变量名.方法名();
编译看左边,运行看右边
好处
实际开发的过程中,父类类型作为方法形式参数,传递子类对象给方法,进行方法的调用,更能体现出多态的扩展性和便利性。
xxxxx(父类类型 变量名),
传入一个子类对象。
使用
- 前提:多态对象把自己看做是父类类型
- 成员变量:使用的是父类的
- 成员方法:由于存在重写现象,所以使用的是子类的
- 静态成员:随着类的加载而加载谁,谁调用就返回谁的
转型
- 向上转型
多态本身就是子类类型向父类类型向上转换的过程,这个过程是默认的。
父类类型 变量名 = new 子类类型();
//如:Animal a = new Cat();
通俗来讲:花木兰替父从军,大家都把花木兰看做她爸,但是实际从军的是花木兰,而且,花木兰只能做她爸能做的事,在军营里是不可以化妆的。
- 向下转型
父类类型向子类类型向下转换的过程,这个过程是强制的。
子类类型 变量名 = (子类类型) 父类变量名;
// 如: Cat c =(Cat) a;
通俗来讲:花木兰打仗结束,就不需要再看做是她爸了,就可以”对镜贴花黄”了
- 使用
当使用多态方法调用方法时,首先检查父类中是否有该方法,如果没有,则编译是错误。也就是说,使用向上转型的的调用方法时,不能调用子类拥有,而父类没有的方法。
这也是多态给我们带来的一点“小麻烦”。所以,想要调用子类特有的方法,必须向下转型。
- 异常
public class Test {
public static void main(String[] args) {
// 向上转型
Animal a = new Cat();
a.eat(); // 调用的是 Cat 的 eat
// 向下转型
Dog d = (Dog)a;
d.watchHouse(); // 调用的是 Dog 的 watchHouse 【运行报错】
}
}
这段代码可以通过编译,但是运行时,却报出了 ClassCastException ,类型转换异常!这是因为,明明创建了Cat类型对象,运行时,当然不能转换成Dog对象的。这两个类型并没有任何继承关系,不符合类型转换的定义。
为了避免ClassCastException的发生,Java提供了 instanceof 关键字,给引用变量做类型的校验。
变量名 instanceof 用于判断数据类型
如果变量属于该数据类型,返回true
如果变量不属于该数据类型,返回false
public class Test {
public static void main(String[] args) {
// 向上转型
Animal a = new Cat();
a.eat(); // 调用的是 Cat 的 eat
// 向下转型
if (a instanceof Cat){
Cat c = (Cat)a;
c.catchMouse(); // 调用的是 Cat 的 catchMouse
} else if (a instanceof Dog){
Dog d = (Dog)a;
d.watchHouse(); // 调用的是 Dog 的 watchHouse
}
}
}
static关键字
定义
它可以用来修饰成员变量和成员方法,被修饰的成员是属于类的,而不是单单属于某个对象。也就是说,既然属于类,就可以不靠创建对象来调用
使用
- 静态变量(类变量)
当static修饰成员变量时,该变量称为类变量。该类的每个对象都共享同一个类变量的值。任何对象都可以更改该类变量的值,单页可以在不创建该类对象的情况下对类变量进行操作。
public class Student {
private String name;
private int age;
// 学生的id
private int sid;
// 类变量,记录学生数量,分配学号
public static int numberOfStudent = 0;
public Student(String name, int age){
this.name = name;
this.age = age;
// 通过 numberOfStudent 给学生分配学号
this.sid = ++numberOfStudent;
}
// 打印属性值
public void show() {
System.out.println("Student : name=" + name + ", age=" + age + ", sid=" + sid );
}
}
public class StuDemo {
public static void main(String[] args) {
Student s1 = new Student("张三", 23);
Student s2 = new Student("李四", 24);
Student s3 = new Student("王五", 25);
Student s4 = new Student("赵六", 26);
s1.show(); // Student : name=张三, age=23, sid=1
s2.show(); // Student : name=李四, age=24, sid=2
s3.show(); // Student : name=王五, age=25, sid=3
s4.show(); // Student : name=赵六, age=26, sid=4
}
}
- 静态方法(类方法)
当static修饰成员方法时,该方法称为类方法。静态方法在声明中有static,建议使用类名来调用,而不需要创建类的对象,调用方式很简单。
//为上边Student类补充一个静态方法
public static void showNum() {
System.out.println("num:" + numberOfStudent);
}
- 静态代码块
也就是定义在成员位置,使用static修饰的代码块{}。
位置处于类中方法外。
执行:随着类的加载而执行且执行一次,优先于main方法和构造方法的执行。
public class Game {
public static int number;
public static ArrayList<String> list;
static {
// 给类变量赋值
number = 2;
list = new ArrayList<String>();
// 添加元素到集合中
list.add("张三");
list.add("李四");
}
}
- 静态调用
public static void main(String[] args) {
// 访问类变量
System.out.println(Student.numberOfStudent);
// 调用静态方法
Student.showNum();
}
final关键字
定义
举个例子,我们能不能随意修改继承API中提供的类,或者改写其内容?显然是不合适的。为了避免这种随意改写的情况,java提供了final关键字,用来修饰不可改变的内容。
修饰什么
- 修饰类
final class 类名 {
}
作用:
查询API发现像 public final class String 、 public final class Math 、 public final class Scanner等,都是被final修饰的,此类不能被修改。
并且被 final 修饰的类不能被继承
- 修饰方法
修饰符 final 返回值类型 方法名(参数列表){
//方法体
}
作用:
继承带有 final 修饰的方法时不能重写,否则编译时就会报错。
- 修饰变量
-
修饰局部变量
- 变量类型为基本类型
基本类型的局部变量,被final修饰后,只能赋值一次,不能再更改。
public class FinalDemo1 { public static void main(String[] args) { // 声明变量,使用final修饰 final int a; // 第一次赋值 a = 10; // 第二次赋值 a = 20; // 报错,不可重新赋值 // 声明变量,直接赋值,使用final修饰 final int b = 10; // 第二次赋值 b = 20; // 报错,不可重新赋值 } }
- 变量类型为引用类型
引用类型的局部变量,被final修饰后,只能指向一个对象,地址不能再更改。但是不影响对象内部的成员变量值的修改。
public static void main(String[] args) { // 创建 User 对象 final User u = new User(); // 创建 另一个 User对象 u = new User(); // 报错,指向了新的对象,地址值改变。 // 调用setName方法 u.setName("张三"); // 可以修改 }
-
修饰成员变量
初始化方法有两种
-
显示初始化
public class User { final String USERNAME = "张三"; private int age; }
-
构造方法初始化
public class User { final String USERNAME ; private int age; public User(String username, int age) { this.USERNAME = username; this.age = age; } }
被final修饰的常量名称,一般都有书写规范,所有字母都大写
-
Object类
定义
java.lang.Object 类是Java语言中的根类,即所有类的父类。它中描述的所有方法子类都可以使用。在对象实例化的时候,最终找的父类就是Object。
如果一个类没有特别指定父类, 那么默认则继承自Object类。
成员方法
- toString()
toString方法返回该对象的字符串表示,其实该字符串内容就是
对象的类型+@+内存地址值
由于toString方法返回的结果是内存地址,而在开发中,经常需要按照对象的属性得到相应的字符串表现形式,因此也需要去重写它。
public class Person {
private String name;
private int age;
@Override
public String toString() {
return "Person{" + "name='" + name + '\'' + ", age=" + age + '}';
}
// 省略构造器与Getter Setter
}
- equals()
equals()方法用来指示其他某个对象是否与此对象”相等“。
如果没有覆盖重写equals方法,那么Object类中默认进行==运算符的对象地址比较,只要不是同一个对象,那么结果必然为false。
如果希望进行对象内容的比较,即所有或指定的部分成员变量相同就判定两个对象相同,则可以覆盖重写equals方法。
import java.util.Objects;
public class Person {
private String name;
private int age;
@Override
public boolean equals(Object o) {
// 如果对象地址一样,则认为相同
if (this == o)
return true;
// 如果参数为空,或者类型信息不一样,则认为不同
if (o == null || getClass() != o.getClass())
return false;
// 转换为当前类型
Person person = (Person) o;
// 要求基本类型相等,并且将引用类型交给java.util.Objects类的equals静态方法取用结果
return age == person.age && Objects.equals(name, person.name);
}
}
与==的区别:
- 对于基本类型,==比较的是值。
- 对于引用类型,==比较的是地址。
- equals不能用于基本类型的比较。
- 如果没有重写equals,equals就相当于==。
- 如果重写了equals方法,equals比较的是对象的内容。
- hashCode()
返回的是一串由JVM给不同对象分配的整数类型的哈希码值。
每个对象的哈希码值都不相同,除非重写hashCode()方法。
- getClass()
返回对象的运行时的类,简单来说就是获取当前对象所属的字节码文件对象。
- finalize()
用于垃圾回收,我们不用手动的去调用,而是由JVM来调用。
当垃圾回收器确定不存在对该对象的更多引用时,由对象的垃圾回收器调用此方法。
- Objects类
它提供了一些方法来操作对象,它由一些静态的实用方法组成,这些方法是null-save(空指针安全的)或者null-tolerant(容忍空指针的),用于计算对象的hashCode,返回对象的字符串表示形式,比较两个对象的。
在比较两个对象的时候,Object的equals方法容易抛出空指针异常,而Objects类中的equals方法就优化了这个问题。
public static boolean equals(Object a, Object b) :判断两个对象是否相等。
package包
定义
写的class类成千上万,为了我们的Java文件不重叠,于是有了包的概念。
特点
- 对类文件进行分类管理
- 给类提供多层命名空间
- 包的定义是用一个关键字package包名
- 写在程序的第一行
- 包也是一种封装的形式
- 定义包名的字母都是小写
导包
当我们用到某个包的类时,用关键字import把包的所有类给导入。这样就不用每次通过包名.类名去调用了,方便了操作,简化了书写。
当导入不同包中的有同名类时,必须指定包名。
为了不至于类名重复,可以使用url来完成定义是唯一的。 (url:域名)
内部类
定义
将一个类A定义在另一个类B里面,里面的那个类A就称为内部类,B则称为外部类。
分类
- 成员内部类
是定义在类中方法外的类。
class Car { //外部类
class Engine { //内部类
}
}
访问特点:
内部类可以直接访问外部类的成员,包括私有成员。
外部类要访问内部类的成员,必须建立内部类的对象。
外部类名.内部类名 对象名 = new 外部类型().new 内部类型();
- 匿名内部类
是内部类的简化写法。它的本质是一个带具体实现的父类或者父接口的匿名的子类对象。开发中,最常用的内部类就是匿名内部类。
特点:
当使用一个接口时,需要以下几步操作:
定义子类->重写接口中的方法->创建子类对象->调用重写后的方法
通俗来讲,我们最终的目的,只是为了调用这个方法,那么是不是可以简化一下,将以上四步合成一步?匿名内部类就是做这样的快捷方式。
前提:
匿名内部类必须继承一个父类或者实现一个父接口
使用方式:
定义接口
public abstract class FlyAble{
public abstract void fly();
}
创建匿名内部类,并调用
public static void main(String[] args) {
/*
1.等号右边:是匿名内部类,定义并创建该接口的子类对象,此时是需要具体实现一个父类或者父接口的,
2.等号左边:是多态赋值,接口类型引用指向子类对象
*/
FlyAble f = new FlyAble(){
public void fly() {
System.out.println("我飞了~~~");
}
};s
//调用 fly方法,执行重写后的方法
f.fly();
}