010-面向对象三大特性
一、static关键字
一、static 修饰变量
1.静态变量的特点
使用 static 修饰变量称为静态变量,静态变量在类加载的时候初始化,并且存储在方法区。
静态变量,是与类相关的,访问时采用“类名.”方式访问,它不同于实例变量需要 new 对象,静态变量是不需要 new 对象,不需要对象参与即可访问,没有空指针异常的发生。
//定义一个测试类
public class Student {
private String name;
private String age;
//因为每个学生都属于同一个学校,因此我们将该属性定义为静态变量
//这样就不用每创建一个对象就在堆内存为该属性开辟一个内存空间了
public static String school = "中山大学";
}
public class test {
public static void main(String[] args) {
//直接采用“类名.”方式调用
System.out.println(Student.school);
}
}
- 什么时候变量声明为实例的,什么时候声明为静态的?
如果这个一个类的所有对象的某个属性值都是一样的,不建议定义为实例变量,浪费内存空间(因为不同对象的实例变量都会在堆内存占据一定的内存空间)。建议定义为类级别特征,定义为静态变量,在方法区中只保留一份,节省内存开销。
二、static 修饰方法
使用 static 修饰方法称为静态方法,静态方法是不需要new对象,直接采用“类名.”调用,注意的是,因为静态方法是不需要对象的,所以里面不能访问实例对象。
//定义一个测试类
public class Student {
private String name;
private String age;
public static String school = "中山大学";
public static void test(){
System.out.println("我是静态方法")
}
}
public class test {
public static void main(String[] args) {
//直接采用“类名.”方式调用
System.out.println(Student.test());
}
}
三、静态代码块
-
语法
static{
java语句;
} -
执行时机
类加载时执行,并且在main方法执行之前执行,只执行一次;
二、this关键字
一、this关键字的特性
- this 是一个引用,this 中保存的是当前对象的内存地址,指向自身,也就是说this代表的是“当前对象”;
- this 只能使用在实例方法中,谁调用这个实例方法,this就是谁;
//定义一个测试类
public class Student {
private String name;
private String age;
public void test(){
this.name = "路明非";
this.age = "25";
}
}
public class test {
public static void main(String[] args) {
//创建Student对象并且调用 test 方法
Student s1 = new Student();
s1.test();
/*上面可以看出,我们创建了 s1 对象,并且调用了该对象的test()方法,因为是s1调用的
所以方法中的this指的是s1对象
*/
}
}
- this不能使用在静态方法中,因为this代表当前对象,静态方法不存在当前对象;
- this.大部分情况下是可以省略的,实例方法中,或者构造方法中,为了区分局部变量和实例变量,这种情况下:this. 是不能省略的;
//定义一个测试类
public class Student {
private String name;
private String age;
student(String name,String age){
//此时this.就不可以省略了,因为在构造函数中,name 和 age 是函数传进来的值
//并不等于当前对象的值,如果不加 this. 就会造成歧义
this.name = name;
this.age = age;
}
public void test(){
//this是可以省略的,因为都处于当前对象中,this.name 就等于 name
name = "路明非";
age = "25";
}
}
二、this()
通过当前的构造方法去调用本类的另一个构造方法,可以使用以下语法格式:
this(实际参数列表);
- 通过一个构造方法1去调用构造方法2,可以做到代码复用;
- 要注意的是:“构造方法1”和“构造方法2” 都是在同一个类当中;
- 对于this()的调用只能出现在构造方法的第一行;
三、面向对象三大特性之 —— 封装
一、封装的作用
- 将类中的安全级别较高的数据封装起来,外部人员不能随意访问,来保证数据的安全性;
- 对于调用的人员来说,就不需要关心代码的复杂实现,只需通过一个简单的入口就可以访问了;
二、如何封装
- 将需要封装的类中的属性私有化,使用private关键字进行修饰;
- 对外提供简单的操作入口
- 写一个不带static实例方法专门来完成一个私有属性读(get);
- 写一个不带static实例方法专门来完成一个私有属性写(set);
get和set方法的书写规范,set方法和get方法要尽量满足以下格式:
1.get方法的要求:
public 返回值类型 get+属性名首字母大写(无参){
return xxx;
}
2.set方法的要求:
public void set+属性名首字母大写(有1个参数){
xxx = 参数;
}
- 封装一个学生类并使用其提供的入口对其进行操作
// 定义一个学生类
package com.xzm.test;
public class Student {
//定义类的属性,并将其私有
private int age; //年龄
private String name; //学生姓名
public Student() {}//定义无参构造方法
public Student(String stuName,int stuAge) {//定义有参构造方法,可以给属性赋值;
age = stuAge;
name = stuName;
}
//为私有属性提供访问接口set 和 get 方法
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
public class test {
public static void main(String[] args) {
Student student = new Student("路明非",24);
/*
* 尝试直接访问student的私有属性,编译不通过报错
* System.out.println("尝试直接访问student的私有属性:"+ student.name);
* */
/*
* 程序运行成功,输出
* 尝试使用student提供的入口的访问私有属性:路明非
* */
System.out.println("尝试使用student提供的入口的访问私有属性:"+ student.getName());
}
}
三、面向对象三大特性之 —— 继承(extends)
一、继承的特性
- B 类去继承 A 类,则称 A 类为超类(superclass)、父类、基类;B 类称为子类(subclass)、派生类、扩展类,如:
class A{}
class B extends A{} - java 中的继承只支持单继承,因此这样写代码:class B extends A,C{}是不允许的,
- java 中虽然不支持多继承,但是有的时候会产生间接继承的效果,例如:class C extends B{},class B extends A{},也就是说 C 直接继承 B,间接继承 A;
- java中规定,子类继承父类,除了构造方法不能继承之外,剩下的都可以继承,但是私有的属性无法在子类中直接访问,只能采用间接的手段来访问(如getXXX()方法);
- java 中的类没有显示继承任何类,则默认继承 Object 类,Object 类是 java 语言提供的根类,也就是说,一个对象与生俱来就有了 Object 类型的所有特性;
// 定义一个学生类
public class Student {
//定义类的属性,也就是学生的共同具有的属性
private int age; //年龄
private String name; //学生姓名
//定义类方法,也就是描述学生所具有的共同行为
public void eat() {
System.out.println("学生吃饭");
}
}
二、继承的作用
- 基本作用:子类继承父类,代码可以得到复用;
- 主要作用:因为有了继承关系,才有了后期的方法覆盖和多态机制;
// 定义一个 Animal 作为父类
public class Animal {
public void eat() {
System.out.println("动物会吃饭");
}
}
// 定义一个 Cat 继承 Animal
public class Cat extends Animal {
//此时 Cat 类已经继承了 Animal 类中的方法了
}
public class test {
public static void main(String[] args){
Cat cat = new Cat();
cat.eat();
}
//此时 cat 可以直接调用 eat()方法,这样就提高了代码的复用率,以后要是其他动物类也用这个功能,直接继承该类就可以了。
}
三、方法的重写
当父类中的方法无法满足子类的业务需求,子类就需要对继承过来的方法进行重写,进行方法的重写需要满足一下的条件:
- 有继承关系的两个类;
- 具有相同的方法名、返回值类型(对于基本数据类型要一致)、形式参数列表;
- 重写时方法的访问权限不能更低;
- 重写时方法抛出异常不能更多;
// 定义一个 Animal 作为父类
public class Animal {
public void eat() {
System.out.println("动物会吃饭");
}
}
// 定义一个 Cat 继承 Animal
public class Cat extends Animal {
//在子类中对方法进行重写
public void eat() {
System.out.println("猫喜欢吃猫粮");
}
}
public class test {
public static void main(String[] args){
Cat cat = new Cat();
cat.eat();
}
//此时 cat 可以直接调用 eat()方法,这样就提高了代码的复用率,以后要是其他动物类也用这个功能,直接继承该类就可以了。
}
进行方法重写时需要注意的事项:
- 方法的重写只针对方法,和属性无关;
- 私有方法无法重写;
- 方法重写只是针对于“实例方法”,“静态方法重写”没有意义;
- 构造方法不能被继承,所以构造方法也不能被重写;
三、面向对象三大特性之 —— 多态
一、多态的基础语法
多态是指父类型的引用指向子类型的对象例如:
class Animal{}
class Cat extends
Animal{}
Animal a = new Cat();
以上这种赋值方式又称为向上转型(自动类型转换),需要注意的时,使用这用赋值方式调用的方法必须是父类所有的方法,不然会报错,但是最后运行的结果是由子类决定的,主要看子类是否有对该方法进行重写。
// 定义一个 Animal 作为父类
public class Animal {
public void eat() {
System.out.println("动物会吃饭");
}
}
// 定义一个 Cat 继承 Animal
public class Cat extends Animal {
//在子类中对方法进行重写
public void eat() {
System.out.println("猫喜欢吃猫粮");
}
//子类特有的方法
public void sleep() {
System.out.println("猫喜欢睡觉");
}
}
public class test {
public static void main(String[] args){
Animal a = new Cat();
//编译时先查看父类是否有该方法,有的话遍历通过,最后执行的是子类中的 eat() 方法,最后输出:猫喜欢吃猫粮
//由于在编译的时候没有在父类找到该方法,因此会报错
a.eat();
}
//此时 cat 可以直接调用 eat()方法,这样就提高了代码的复用率,以后要是其他动物类也用这个功能,直接继承该类就可以了。
}
通过上面的了解,我们可以得出,向上转型时调用的方法需要是子类和父类同时拥有的,不然就编译不通过,最后执行的是子类中的方法,那么如果我们需要执行子类中特有的方法,我们需要如何做呢,这就涉及的类之间的另一种转型方式了,那就是向下转型:
向下转型,又被称为强制类型转换, 通过该方式将 a 进行强转为子类 Cat ,这样就可以调用子类中特有的方法了 Cat c = (Cat)a ;
- 注意的是向下转型存在风险,容易出现ClassCastException,因此,在向下转型之前一定要使用 instanceof 运算符进行判断;
public class test {
public static void main(String[] args){
Animal a = new Cat();
//编译时先查看父类是否有该方法,有的话遍历通过,最后执行的是子类中的 eat() 方法,最后输出:猫喜欢吃猫粮
//由于在编译的时候没有在父类找到该方法,因此会报错
a.eat();
//进行向下转型,不报错
if(a instanceof Cat){
Cat c = (Cat) a;
c.eat();
}
}
二、多态在开发中的作用
// 定义一个宠物类
public class Pet {
}
// 定义猫继承宠物类
public class Cat extends Pet {
}
// 定义狗继承宠物类
public class Dog extends Pet {
}
// 测试
public class test {
public static void main(String[] args) {
//加入我们有个方法,需要根据传入不同类型的宠物,而调用不同方法,那我们就需要创建很多重载方法
//这样显然是不行的,会导致扩展力很差
Dog d = new Dog();
Cat c = new Cat();
public void feed(Dog d){}
public void feed(Cat c){}
......
//那么我们可以采用多态来解决
Dog d = Pet pet();
Cat c = Pet pet();
public void feed(Pet pet){}
}
四、super关键字
一、super的语法
- super()
- super.
二、super. 的作用
在父和子中有同名的属性,或者说有相同的方法,如果此时想在子类中访问父中的数据,必须使用“super.”加以区分;
- super.属性名 【访问父类的属性】
- super.方法名(实参) 【访问父类的方法】本
- super能出现在实例方法和构造方法中
- super不能使用在静态方法中
- super. 大部分情况下是可以省略的
- super 不是引用。super也不保存内存地址
- super也不指向任何对象
三、super()和super(实参)
只能出先在构造方法的第一行,通过当前构造方法去调用“父类”中的构造方法,目的是创建子类对象时,先初始化父类型特征,并不是创建新对象。实际上对象只创建了1个
- 当一个构造方法的第一行既没有this()也没有super()时,会默认有一个super(),表示通过当前子类的构造方法调用父类的无参构造方法,所以必须保证父类的无参构造方法是存在的;
- this()和super()是不能共存的,它们都是只能出现在构造方法第一行;
- 在构造方法执行过程中一连串调用父类的构造方法,父类的构造方法又继续向下调用他的父类构造方法,但是实际上,对象只是创建了一个;
相关链接:
链接: 009-了解面向对象.
链接: 008-Java中方法的使用(进阶篇).
链接: 007-Java中方法的使用(基础篇).