1 类
类指描述一类事物或将事物看成是一个分类。
设计一个类:
class 类名{
字段(成员变量)
方法(具体的功能)
}
把字段和方法合并到类中的过程称为封装。
1.1 类的设计
设计一个学生类:
public class Student {
// 字段
String name;
int age;
// 方法
void eat() {
}
void sleep() {
}
}
这样一个类相当于一个模板,不需要独立启动,无需主方法。
2 对象
对象(object)可以定义为同时包含数据和行为的一个实体,也称为实例(instance),对象时一个类中的具体个体。面向对象的四个基本特征:封装、抽象、继承、多态。
2.1 对象的创建
创建一个对象:
类名 对象名 = new 类名();
创建对象,表示创建某一类事物的一个具体对象。
创建Student对象:
public class StudentTest {
// 测试类,负责独立的启动,需要主方法
public static void main(String[] args) {
// 创建一个对象,然后用一个变量s存储起来
Student s = new Student();
// 给s对象的属性赋值
s.name = "小明";
s.age = 27;
// 操作s对象的方法
s.eat();
s.sleep();
System.out.println(s.name + "今年" + s.age + "了");
}
}
2.2 堆栈内存图分析
1.创建一个对象,就会在堆中生成一个空间,空间中有与类相同的字段;(相当于复制一个模版)
2.在栈中有一个变量存储对象的地址;
3.给对象赋值时,相当于将数据存入堆中字段上。
2.3 匿名对象和对象生命周期
匿名对象
创建一个对象没有变量来接受(在堆中开辟了空间,在栈中没有引用),如:
new Student();
使用场景1:调用一次非静态方法
Student s = new Student();
s.eat();
————————>
new Student().eat();
使用场景2:把匿名对象作为一个实参传递
Student s = new Student();
eat(s);
————————>
eat(new Student());
对象的生命周期
new Student(); new的时候就开始,当对象失去所有的引用,没有变量再指向它时结束。
3 封装
封装:将对象的状态和行为存放在一个独立的模块中(类),对象的状态(属性)由这个对象自己的行为(方法)来读取和改变。
访问private私有字段(JavaBean):
public class Student {
// 定义两个private变量
private String name;
private int age;
// 对象的属性由这个对象自己的方法来读取和改变
// 赋值
public void setName(String s) {
name = s;
}
// 取值
public String getName() {
return name;
}
public void setAge(int i) {
age = i;
}
public int getAge() {
return age;
}
}
如果字段类型为Boolean,get变为is。
public class StudentTest {
public static void main(String[] args) {
// 创建对象
Student s = new Student();
// 赋值
s.setName("小明");
s.setAge(25);
// 获取值
String name = s.getName();
int age = s.getAge();
System.out.println(name+","+age);// 小明,25
}
}
给对象赋值的方法
1.直接访问字段赋值:字段一般都会私有化;
2.通过构造方法赋值:只能创建对象时赋值,一旦赋值无法修改;
3.通过setter方法赋值:可以重复使用;先通过构造方法赋初始值,后通过setter修改。
4 抽象
抽象:找出一些事物的相似之处,然后将这些事物归为一类。
抽象类
1.使用abstract修饰的类,其本质也是一种类;
2.抽象类中可以有(字段、方法、构造方法),此外抽象类中还可以有抽象方法;
3.抽象类不能创建对象(实例化);
4.抽象类一般作为父类。
抽象方法
1.使用abstract修饰的方法,没有方法主体;
2.抽象方法必须存在于抽象类中(接口也可),不能放在普通类中;
3.抽象的子类中必须覆写父类中的所有抽象方法(或子类也是抽象类);
4.抽象方法需要被继承,所以不能是静态的或私有的。
5 继承
5.1 继承
继承:在定义或实现一个类时,可以在已有类的基础上进行,把已有类所定义的内容作为自己的内容,并加入新的内容或修改原有方法让其适应特殊的需要。继承是从一般到特殊的关系,是子类对父类的拓展。
在创建一个类时,总是在继承,除非已明确指出继承的父类,否则就是隐式地从Java根类Object进行继承。
概念理解
泛化:将不同子类的共性抽象成父类的过程;
特化:在原有父类的基础上加入一些个性的过程;
其原则是父类放共性,子类放个性。
继承的语法格式
class A{}
class B extends A{}
A 就是B的父类(SuperClass),B是A的子类(SubClass)。
继承内容
父类中非私有的字段和方法都可以被继承(访问权限角度),构造方法不能被继承。
类的继承特点
1.Java中类继承只能是单继承,但支持多重继承,每个java类(除了Object)有且只有一个直接父类;
2.如果一个类没有显示的继承另外一个类,那该类的直接父类默认为Object(根类)。
继承与组合关系
1.继承关系是is-a关系,父类的所有非私有的方法和字段都被子类无条件继承,子类不能选择。
2.组合关系是has-a关系,组合类中包含了外部类的对象,组合类可以调用外部类必须的方法。
5.2 父类的初始化
Java会自动在子类的构造器中插入对父类构造器的调用,以完成父类的初始化。
class Art{
Art(){
System.out.println("父类Art");
}
}
class Drawing extends Art{
Drawing(){
System.out.println("子类Drawing");
}
}
class Cartoon extends Drawing{
Cartoon() {
System.out.println("子类Cartoon");
}
public static void main(String[] args) {
Cartoon c =new Cartoon();
}
}
运行结果:父类Art 子类Drawing 子类Cartoon
子类的构造器会调用父类的构造器。
6 多态
多态:一个事物的多种形态。
多态的作用:可以屏蔽不同子类对象之间的差异性。
例如:Animal a = new Cat();相当于把一个子类类型对象存储到父类类型变量中,该过程称为向上转型。
6.1 多态编译运行过程
Animal a = new Cat();
编译时类型:左边的类型,编译器编译时查看的类型
运行时类型:右边的类型,运行时对象真实的类型
编译运行过程分析
public class Animal {
void eat(){}
}
public class Cat extends Animal {
}
public class Test {
public static void main(String[] args) {
Animal a = new Cat();
a.eat();
}
}
编译:如果Animal是Cat的父类,编译器通过;
编译时会到编译时类型Animal中找是否有eat方法,如果没有,会沿父类向上查找,若没找到,编译报错;
运行:先到运行时类型中查找eat方法,如果有就执行,否则向上查找父类中eat方法并运行。
static修饰类中方法
public class Animal {
static void eat(){
System.out.println("eat");
}
}
public class Cat extends Animal {
static void eat(){
System.out.println("猫粮");
}
}
public class Test {
public static void main(String[] args) {
Animal a = new Cat();
// eat是静态方法,应该使用类名.eat(),这句代码编译完毕之后其实就是:Animal.eat()
a.eat(); // eat
}
}
父类方法被static修饰,父类方法被隐藏,不能被覆写,此时变量调用eat方法时不具备多态特征,起结果为eat,而不是猫粮。即某个方法是静态的,它的行为就不具备多态性。
6.2 多态方法的调用以及传递
定义一个父类Cat:
public class Cat {
void eat() {
System.out.println("吃猫粮");
}
}
定义一个子类DingDang:
public class DingDang extends Cat {
@Override
void eat() {
System.out.println("吃铜锣烧");
}
}
定义一个子类Garfield:
public class Garfield extends Cat {
@Override
void eat() {
System.out.println("吃炸鱼");
}
}
定义一个Person对象:
public class Person {
// 统一喂猫的方法
public void feed(Cat c) {
// 调用eat方法
c.eat();
}
}
public class Test {
public static void main(String[] args) {
// 创建一个Person对象
Person p = new Person();
// 创建一个DingDang对象
DingDang d = new DingDang();
p.feed(d); // 调用喂猫的方法,将叮当猫传入参数中(吃铜锣烧)
// 创建一个Garfield对象
Garfield g = new Garfield();
p.feed(g); // (吃炸鱼)
}
}
编译或传递的过程传递的是对象地址,看哪个对象真正调用了eat方法。
6.3 引用类型转换
引用类型转换过程中,子类可以转为父类,父类不能自动转为子类。
public class Animal {}
public class Person extends Animal {}
子类转父类:
Animal a = new Person();
父类转子类:
Person p = new Animal();//Type mismatch: cannot convert from Animal to Person
编译报错Type mismatch: cannot convert from Animal to Person
Animal a = new Person();
Person p2 = a; //Type mismatch: cannot convert from Animal to Person
编译报错Type mismatch: cannot convert from Animal to Person
父类强转子类
public class Cat {}
public class DingDang extends Cat {
void eat(){
System.out.println("吃铜锣烧");
}
}
public class Test {
public static void main(String[] args) {
Cat c = new DingDang();
// 强转之前先要判断对象的真正类型
if (c instanceof DingDang) {
// 将变量c的类型强转为DingDang,再调用方法
DingDang d = (DingDang) c;
d.eat();
} else {
System.out.println("强转类型不匹配");
}
}
}
进行强制转换之前判断被转换的对象的真正类型是否是希望转成的类型,判断对象的真正类型的方式:
Object类中有一个getClass()方法:instanceof
a instanceof B:表示判断a对象是否是B类型的,结果是一个布尔值,如果a的真正类型是B 或者是B的子类结果都是true。