六、构造方法
构造方法是一种特殊的方法,也称之为构造函数 或构造器constructor
作用:用于创建和初始化对象
特点:
- 方法名必须和类名相同
- 没有返回值,也不能写作void
- 当创建类的对象时,会自动调用构造方法
- 一个类中可以有多个构造方法(构造方法重载)
- 如果没有自定义构造方法,则类中默认会有一个无参的构造方法
- 如果自定义了构造方法,则默认的无参构造方法就没有了,可以自己写无参的构造方法(建议)
- 可以使用
this(参数)
调用当前类中的其他构造方法,但只能出现在构造方法的第一行
public static void main(String[] args) {
Dog dog = new Dog();
Dog dog2 = new Dog("豆豆", 3, "金毛");
dog2.run();
dog2.show();
}
}
class Dog {
// 成员属性
String name;
int age;
String breed;
// 成员方法
public void run() {
System.out.println("狗狗正在奔跑....");
}
public void show() {
System.out.println("姓名:" + name + ",狗龄:" + age + ",品种:" + breed);
}
// 构造方法
public Dog() {
System.out.println("这是一个无参的构造方法");
}
// 带参构造方法
public Dog(String name, int age) {
this.name = name;
this.age = age;
System.out.println("这是一个带两个参数的构造方法");
}
public Dog(String name, int age, String breed) {
this(name, age);// 调用构造方法,必须位于第一行
this.breed = breed;
System.out.println("这是一个带三个参数的构造方法");
}
七、包package
包的作用:
- 区分同名的类
- 类比较多时便于管理
- 便于访问控制
声明包:
- 用来指定当前类所在包
- 使用package,写法:
package 包名
- 必须位于第一行(注释除外)
命名规范:
- 采用组织的反向域名
- 使用小写字母,不能以点号开头结尾
- com.baidu.fany
导入包:
- 当需要使用其他包中的类时必须导入
- 使用import ,写法:
import 包名.类名;
或import 包名.*;
- 必须位于包声明的下面
常用的包:
- java.lang包含java的核心类,如System、String、Math等,该包默认会被自动导入,当使用其中的类,无需import导入
- java.util 包含实用的工具类,如Scanner、Arrays等
- java.io 包含输入/输出操作的类
- java.sql 包含数据库操作的类
- java.net 包含网络操作的类
- java.awt 包含图形窗口操作的类
注意:定义类时务必指定包名,严禁将类直接放在src下(default package 默认包,会导致类无法被引用)
八、访问修饰符
用来控制访问范围,可以修饰属性、方法、类等
修饰符 | 同一个类 | 同一个包内的类 | 不同包的子类 | 不同包并且不是子类 |
---|---|---|---|---|
public公开的 | 是 | 是 | 是 | 是 |
protected 受保护的 | 是 | 是 | 是 | 否 |
默认 | 是 | 是 | 否 | 否 |
private私有的 | 是 | 否 | 否 | 否 |
总结:
- public:在哪都能访问
- protected:只有不同包并且非子类不能访问,其他都能访问
- 默认:只有本包中可以访问
- private:只有本类中可以访问
范围从大到小的顺序:public>protected>默认>private
九、对象的初始化顺序
1.类加载
概念:将class文件读取加载到JVM内存中的过程,称为类加载
类加载的时机:
- 创建类的对象
- 调用类的静态属性或静态方法
- 执行main方法时,main方法所在的类会被加载
- 通过反射调用类
Class.forName("包名.类名")
2.类的成员
类中的成员:
- 属性(实例属性、静态属性)
- 方法(实例方法、静态方法)
- 构造方法
- 代码块
- 静态代码块:使用static修饰的代码块
注:static关键字可以修饰变量、方法、代码块、匿名类等
3.初始化顺序
步骤:
-
对象所在的类被加载
执行静态属性和静态代码块(根据定义的顺序,从上往下执行)
特性:
-
静态变量在类加载时初始化,且在内存中值分配一块空间
-
静态代码块在类加载时执行,且只执行一次(类只会被加载一次),主要用于初始化静态属性
-
静态代码块中不能初始化实例变量,只能初始化静态变量
-
-
对象被创建
执行实例属性和代码块(根据定义的顺序,从上往下执行)
执行构造方法
总结:执行先后顺序:
静态代码块>代码块>构造方法
十、面向对象的三大特征
1.封装
1.1Java代码规范
- 将类的属性封装在类中,不允许在类的外部直接访问,保护数据的安全,使内容可控
- 只能通过被授权的方法才能对数据进行访问
- 类的基本原则就是封装
1.2实现方式
步骤:
-
将属性私有化
使用private修饰属性
-
提供对外访问的方法,用于赋值、取值
使用public修饰方法
方法命名规范:
-
赋值方法:以set开头,后面属性名,如setXxx,称为setter方法(属性名首字母大写)
-
取值方法:以get开头,后面属性名,如getXxx
如果是boolean类型属性,可以是getXxx,也可以是isXxx
如果属性只具有getter方法,则表示该属性是只读的,在外部只能读取
如果属性只具有setter方法,则表示该属性是只写的,在外部只能修改
-
public static void main(String[] args) {
User user = new User();
user.setAge(19);
user.setName("老王");
user.setSex("妖");
user.setMarried(false);
System.out.println(user.getAge());
System.out.println(user.getName());
System.out.println(user.getSex());
System.out.println(user.isMarried());
user.show();
}
class User {
private String name;
private int age;
private String sex;
private boolean married;
// 赋值
public void setAge(int age) {
if (age >= 1 && age <= 100) {
this.age = age;
} else {
System.out.println("age无效,已设置为默认值18");
this.age = 18;
}
}
// 取值
public int getAge() {
return age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public void setSex(String sex) {
// if(sex=="男"||sex=="女")
if (sex.equals("男") || sex.equals("女")) {
this.sex = sex;
} else {
System.out.println("性别有误!已设置为女");
this.sex = "女";
}
}
public String getSex() {
return sex;
}
public void setMarried(boolean married) {
this.married = married;
}
public boolean isMarried() {
return married;
}
public void show() {
System.out.println("编号:" + id + ",姓名:" + name + ",年龄:" + age + ",性别:" + sex);
}
}
2.继承
2.1概念
可以让一个类继承自另一个类,此时该类会继承另一个类中的属性和方法 继承而得到的类称为子类,被继承的类称为父类(超类/基类)
继承是一种is a
的关系比如:cat is a animal
或student is a person
2.2实现方式
语法:
class 子类 extends 父类{
}
作用:
- 代码复用:将多个子类中相同的属性和方法放到父类中
- 功能扩展:子类可以有自己独特的属性和方法
特性:
- java只支持单继承,不支持多继承,但支持多层继承,即继承的传递
- java中所有的类都直接或间接继承了java.long.Object类(后面会详细讲)
2.3继承的范围
子类可以继承父类的哪些成员?
- 父类的public、protected、默认修饰的属性和方法可以被子类继承(同包的情况下)
- 父类 的private修饰的属性和方法不能被子类继承
- 父类中的构造方法不能被子类继承(构造方法名必须与类名相同,而子类名称不可能与父类相同)
2.4构造方法的调用
调用过程:
- 创建子类对象是默认会自动调用父类无参的构造方法(此时必须保证父类中有无参的构造方法)
- 从子类开始,逐层往上调用父类的构造方法,直到Object类,然后在层层返回到子类中,执行后续代码
- 创建子类对象时必须会调用父类的构造方法,而调用父类构造方法也就创建了父类的对象,所以当创建子类对象时,其实也创建了父类对象,只不过这个父类对象并不是独立存在的,而是和子类对象合为一个整体
super 关键字:
- 表示父类对象的引用,只能在子类中使用
- 可以使用
super()
调用父类的构造方法,必须位于第一行 - 可以使用
super.
访问父类的属性和方法,解决父类和子类中同名的问题
this关键字:
- 表示当前对象的引用,只能在方法中使用
- 可以使用
this()
访问当前类中重载的构造方法,必须位于第一行 - 可以使用
this.
访问本类中的属性和方法,解决全局变量和局部变量同名的问题
注:调用构造方法时this()和super()都只能放在第一行,所以不能同时使用
public static void main(String[] args) {
Pig pig=new Pig("佩奇","女",2,80);
pig.show();
}
/*
* 父父类
*/
class Live{
public Live() {
System.out.println("父父类中无参的构造方法-------------");
}
}
/*
* 父类
*/
class Animal extends Live{
String name="李四";
String sex;
public Animal() {
System.out.println("父类中无参的构造方法-------");
}
public Animal(String name, String sex) {
super();
this.name = name;
this.sex = sex;
System.out.println("父类中带参的构造方法---------");
}
public void show() {
System.out.println("父类中的show方法---------------");
}
}
/*
* 子类
*/
class Pig extends Animal{
int age;
double weight;
String name="张三";
public Pig(){
System.out.println("子类中无参的构造方法-------------");
}
public Pig(int age,double weight) {
super();//调用父类中无参的构造方法
this.age=age;
this.weight=weight;
System.out.println("子类中带两个参数的构造方法-------");
}
public Pig(String name, String sex,int age,double weight) {
super(name,sex);
this.age=age;
this.weight=weight;
System.out.println("子类中带四个参数的构造方法-------");
}
public void show() {
System.out.println(age);
System.out.println(this.age);
System.out.println(name);
// System.out.println(this.name);
System.out.println(super.name);
super.show();
}
}
2.5方法重写
子类中的方法,与父类中的方法的名称、参数列表、返回值类型一样,我们就说子类重写了父类的方法,称为方法重写override
- 在父子类之间
- 方法名相同
- 参数列表也相同
- 返回值类型相同或是子类
- 访问修饰符不能缩小,即访问权限不能缩小
- 子类不能抛出比父类更大的异常
作用:用来重新定义子类的行为,解决父类和子类的差异性
public static void main(String[] args) {
Cat cat=new Cat();
cat.name="猫咪";
cat.cry();//调用的是子类重写后的方法
}
/*
* 父类
*/
class Pet{
String name;
String sex;
int health;
public void cry() {
System.out.println("俺是宠物,俺能叫----------");
}
public Pet show() {
System.out.println("我是一只宠物");
return new Pet();
}
}
class Cat extends Pet {
@Override //该注解表示当前方法是一个重写方法,可有可无
public void cry() {
System.out.println("俺是猫,喵喵喵");
}
@Override
public int sum(int num1, int num2) {
return super.sum(num1, num2);
}
@Override
public Cat show() {
System.out.println("我是一只猫");
return new Cat();
}
}
面试:方法重载和方法重写的区别?
3.多态
3.1概念
多态是具有表现多种形态的能力的特征,即一种事物,具有多种形态
回顾?基本数据类型之间的转换
- 自动类型转换
- 强制类型转换
引用类型之间的转换
3.2引用类型之间的转换
两种:
-
自动类型转换
将子类对象转换为父类,如student——>person
将父类的引用指向子类的对象,称为向上转型,会自动进行类型的转换
特性:
通过父类引用变量调用的方法是子类重写后的方法,不是父类的方法
通过父类引用变量是无法访问子类特有的属性和方法
-
强制类转换
将一个指向子类对象的父类引用赋给一个子类的引用,称为向下引用,必须进行强制类型转换
语法:
(子类类型)指向子类对象的父类引用
特性:
向下转型后,才可以访问子类特有的属性和方法
必须转化为父类指向的真实的子类类型,否则会出现类型转换异常ClassCastException
向下转型是不安全的,可以再转换时使用
instanceof
操作符判断对象的类型,避免类型转换异常public static void main(String[] args) { /* * 自动类型转换 */ Person person = new Teacher();// 向上转型 person.name = "tom"; person.show();// 调用子类重写后的方法 /* * 强制类型转换 // */ // Teacher t=(Teacher)person;//向下转型 // t.school="九江学院"; // t.teach(); if (person instanceof Doctor) {// 判断一个对象是否属于某个类或实现了某个接口,结果是true或false Doctor d = (Doctor) person; d.hospital = "九江学院附属医学院"; d.operate(); } } class Person { String name; public void show() { System.out.println("我是一个人"); } } class Teacher extends Person { String school;// 所在学校 // 重写父类的方法 public void show() { System.out.println("我叫" + name + ",我是一个老师"); } // 子类特有的方法 public void teach() { System.out.println("我正在" + school + "进行教学------"); } } class Doctor extends Person { String hospital;// 所在医院 // 重写父类的方法 public void show() { System.out.println("我叫" + name + ",我是一个医生"); } // 子类特有的方法 public void operate() { System.out.println("我正在" + hospital + "做手术"); } }
3.3多态的实现
将父类作为方法形参,将子类的对象作为方法实参,从而实现多态
案例:主人与宠物、玩耍、喂养
- 主人类Master
- 宠物Pet——>Dog、Cat…
- 食物Food——>Bone、Fish…
3.4总结
实现多态的条件:
- 继承的存在(继承是多态的基础,没有继承就没有多态)
- 子类要重写父类的方法(多态下调用的是子类重写后的方法)
- 父类的引用变量指向子类对象(向上转型)
多态的有点:
-
减少代码量
-
提高可扩展性和可维护性