封装
概述
将类的某些信息隐藏在内部,不允许外界程序直接访问,而是**通过该类提供的方法**
来实现对隐藏信息的操作和访问
封装在java中的体现
**方法**
就是一种封装- 关键字
**private**
也是一种封装
封装的优点
- 良好的封装能够减少耦合。
- 类内部的结构可以自由修改。
- 可以对成员变量进行更精确的控制。
- 隐藏信息,实现细节。
封装的分类
属性封装
- 属性改私有
**private**
- 创建公有的
**get/set**
方法 - 对属性合法性增加判断
方法的封装
涉及到代码设计
封装的步骤
- 使用
private
关键字来修饰成员变量。 对需要访问的成员变量,提供对应的一对
getXxx
方法(无参数,有返回值类型) 、setXxx
方法(有参数,无返回值类型)。封装操作【private关键字】
**private**
是一个权限修饰符,代表最小权限- 可以修饰
**成员变量**
和**成员方法**
- 被
**private**
修饰后的成员变量和成员方法,只在**本类**
中才能访问private的使用格式
private 数据类型 变量名;
使用 private 修饰
**成员变量**
,代码如下:
public class Student {
private String name;
private int age;
}
提供
**getXxx**
方法 /**setXxx**
方法,可以访问成员变量方法
- 对于
Getter
来说,**不能有参数**
,返回值类型和成员变量对应 - 对于
Setter
来说,**不能有返回值**
,参数类型和成员变量对应
📝 例1
public class Student {
private String name;
public void show(){
System.out.println("我叫:" +name);
}
//这个方法,专门向name设置数据
public void setName(String n){
name = n;
}
//这个方法专门私有获取name数据
public String getName(){
return name;
}
}
public class Person {
public static void main(String[] args) {
Student student = new Student();
student.setName("小懒");
student.show();//我叫:小懒
}
}
📝 例2
public class Student {
private int age;
public void show(){
System.out.println("我的年龄"+age);
}
//这个方法,专门向age设置数据
public void setAge(int num){
if(num < 120 && num > 9) {
age = num;
}else {
System.out.println("数据不合理");
}
}
//这个方法专门私有获取age数据
public int getAge(){
return age;
}
}
public class Person {
public static void main(String[] args) {
Student student = new Student();
student.setAge(3);
student.show();
}
}
注意:对于基本类型当中的
**boolean**
值,**get**
方法一定要写成**isXxx**
,而setXxx规则不变
标准代码——JavaBean
JavaBean
是 Java语言编写类的一种标准规范。符合 JavaBean
的类,要求类必须是具体的和公共的,并且具有无参数的构造方法,提供用来操作成员变量的set
和 get
方法。
public class ClassName{
//成员变量
//构造方法
//无参构造方法【必须】
//有参构造方法【建议】
//成员方法
//getXxx()
//setXxx()
}
编写符合 JavaBean 规范的类,以学生类为例,标准代码如下:
public class Student {
//成员变量
private String name;
private int age;
//构造方法
public Student() {}
public Student(String name,int age) {
this.name = name;
this.age = age;
}
//成员方法
publicvoid setName(String name) {
this.name = name;
}
public String getName() {
return name;
}
publicvoid setAge(int age) {
this.age = age;
}
publicint getAge() {
return age;
}}
测试类,代码如下:
public class TestStudent {
public static void main(String[] args) {
//无参构造使用
Student s= new Student();
s.setName("柳岩");
s.setAge(18);
System.out.println(s.getName()+"‐‐‐"+s.getAge());
//带参构造使用
Student s2= new Student("赵丽颖",18);
System.out.println(s2.getName()+"‐‐‐"+s2.getAge());
}
}
访问权限修饰符
在Java中提供了四种访问权限,使用不同的访问权限修饰符修饰时,被修饰的内容会有不同的访问权限
public
: 公共的
protected
: 受保护的
default
: 默认的
private
: 私有的
不同权限的访问能力
可见,public
具有最大权限。private
则是最小权限。
编写代码时,如果没有特殊的考虑,建议这样使用权限:
- 成员变量使用
private
,隐藏细节 - 构造方法使用
public
,方便创建对象 - 成员方法使用
public
,方便调用方法不加权限修饰符,其访问能力与default修饰符相同
static关键字
特点:
1、static是一个修饰符,用于修饰成员(成员变量,成员方法),static修饰的**成员变量**
,称之为**静态变量**
或**类变量**
。
2、static修饰的成员被所有的对象共享
。
3、static优先于对象存在,因为static的成员随着类的加载就已经存在
。
4、static修饰的成员多了一种调用方式,可以直接被类名所调用,**类名.静态成员**
5、static修饰的数据是共享数据,对象中的存储的是特有的数据。
static修饰成员变量
如果一个成员变量使用了
static
关键字,那么这个变量不在属于对象自己,而是属于所在类。多个对象共享同一份数据
public class Student {
private String name;//姓名
private int age;//年龄
private int id;//学号
static String room;//教室
private static int idCounter = 0;//学号计数器,每当new了一个新对象的时候,计数器++
public Student() {
this.id = ++idCounter;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public Student(String name, int age) {
this.name = name;
this.age = age;
this.id = ++idCounter;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
public class DemoStaticField {
public static void main(String[] args) {
Student one = new Student("小明",18);
one.room = "101教室";
System.out.println("姓名:"+one.getName()+",年龄" + one.getAge()+",教室 "+one.room +",学号"+one.getId());
Student two = new Student("小红",19);
System.out.println("姓名:"+two.getName()+",年龄" + two.getAge()+",教室 "+two.room+",学号"+two.getId());
}
}
//姓名:小明,年龄18,教室 101教室,学号1
//姓名:小红,年龄19,教室 101教室,学号2
static修饰成员方法
- 一旦使用
**static修饰成员方法**
,就成为了**静态方法**
,静态方法不属于对象,而是属于**类**
的 - 如果没有static关键字,那么必须首先创建对象,然后通过对象才能使用它
- 如果有了static关键字,那么就不需要创建对象,直接通过
**类名称**
来使用它 - 无论是成员变量还是成员方法,如果有了static,都推荐使用类名称进行调用
- 静态变量:
**类名称.静态变量**
- 静态方法:
**类名称.静态方法**
注意事项:
1、静态不能直接访问非静态 ,非静态可以访问静态
原因:因为在内存中是【先】有静态内容【后】有的非静态内容 “先人不知道后人,但是后人不知道先人”
2、静态方法中不能用**this**
原因:this代表当前对象,通过谁调用的方法,谁就是当前对象。而static不依附于任何对象,既然都没有对象,就谈不上this了
public class Myclass {
int num;//成员变量
static int numStatic;//静态变量
//成员方法
public void method(){
System.out.println("这是一个成员方法!");
//成员方法可以访问成员变量
System.out.println(num);
//成员方法可以访问静态变量
System.out.println(numStatic);
}
public static void methodStatic(){
System.out.println("这是一个静态方法!");
//静态方法可以访问静态变量
System.out.println(numStatic);
//静态不能直接访问非静态【重点】
System.out.println(num);//错误写法
//静态方法中不能使用this关键字
System.out.println(this);//错误写法
}
}
public class DemoStaticMethod {
public static void main(String[] args) {
Myclass obj = new Myclass();//首先创建对象
//然后才能使用没有static关键字的内容
obj.method();
//对于静态方法来说,可以通过对象名进行调用,也可以通过类名称来调用
obj.methodStatic();//正确,不推荐
Myclass.methodStatic();//正确,推荐
// 对于本来当中的静态方法,可以省略类名称
myMethod();
DemoStaticMethod.myMethod();//完全等效
}
public static void myMethod(){
System.out.println("自己的方法!");
}
}
静态内存图
静态代码块:
在程序执行之前,就会优先执行
特点:当第一次执行本类时,静态代码块执行**唯一的一次**
作用:用来一次性对静态成员变量进行赋值
静态代码块格式:
public class 类名称{
static{
//静态代码块
}
}
public class Person {
static {
System.out.println("静态代码块执行");
}
public Person(){
System.out.println("构造方法执行!");
}
}
public class DemoStatic {
public static void main(String[] args) {
Person one = new Person();
Person two = new Person();
}
}
成员变量和静态变量的区别:
生命周期的不同:
成员变量:随着**对象**
的创建而存在,随着对象的回收而释放。
静态变量:随着**类**
的加载而存在,随着类的消失而消失。
调用方式不同:
成员变量:只能被**对象调用**
静态变量:可以被**对象调用**
,也可以用**类名调用**
。(推荐用类名调用)
别名不同:
成员变量:也称为**实例变量**
。
静态变量:称为**类变量**
。
数据存储位置不同:
成员变量:数据存储在**堆内存**的对象中
,所以也叫对象的特有数据。
静态变量:数据存储在**方法区**
(共享数据区)的静态区,所以也叫对象的共享数据。
变量的分类
成员变量:普通对象拥有
类变量(用static修饰的变量):类拥有
局部变量:局部拥有
方法的分类
成员方法:普通方法
类方法(用static修饰的方法):类拥有
特殊:
this关键字:动态对象,当前对象的意思
快捷键:
alt+enter:建议
alt+insert:自动生成代码
强行停止程序
System.exit(0);//强行结束程序
继承:
java类与类之间实现代码复用的手段之一,类和类符合使用继承的条件是必须满足**is-a**
关系
在继承中一个子类只能有一个直接父类,(**只支持单继承**
)如果要实现复用多个类代码的复用可以使用继承体系
如果类 A 是类 B 的父类,而类 B 是类 C 的父类,我们也称类 C 是 A 的子类,类 C 是从类 A 继承而来的。在 Java 中,类的继承是单一继承,也就是说,一个子类只能拥有一个父类。
不能被继承的父类成员:
- 构造方法
- 静态成员,包括静态方法静态属性
- 子类与父类不在同包,父类默认权限的成员
继承的特性:
- 子类拥有父类
非 private 的属性、方法
。 - 子类可以拥有自己的属性和方法,即子类可以对父类进行扩展。
- 子类可以用自己的方式实现父类的方法。
- Java 的继承是
单继承
,但是可以多重继承,单继承就是一个子类只能继承一个父类,多重继承就是,例如 B 类继承 A 类,C 类继承 B 类,所以按照关系就是 B 类是 C 类的父类,A 类是 B 类的父类,这是 Java 继承区别于 C++ 继承的一个特性。 - 提高了类之间的耦合性(继承的缺点,耦合度高就会造成代码之间的联系越紧密,代码独立性越差)。
继承条件下构造方法的调用规则
- 子类构造方法没有通过super显式调用父类的有参构造方法,也没通过this显式调用自身其他构造方法,系统默认调用父类的无参构造方法
- 子类构造方法通过super显式调用父类的有参构造方法,执行父类相应构造方法,而不执行父类无参构造方法
- 子类构造方法通过this显式调用自身的其他构造方法,在相应构造方法中应用以上两条规则
继承的关键字
`**extends**`
定义一个父类的格式(一个普通类的格式):
public class 父类名称{
//......
}
定义一个子类的格式
pulic class子类名称 extends 父类名称{
//........
}
//定义一个员工类
public class Employee {
public void method(){
System.out.println("方法执行!!!");
}
}
//定义一个教师类
public class Teacher extends Employee {
}
//定义一个助教类
public class Zhujiao extends Employee {
}
public class DemoExtends {
public static void main(String[] args) {
//创建一个子类对象
Teacher tea = new Teacher();
//Teacher类当中虽然什么都没有写,但是会继承来自父类的method方法
tea.method();//方法执行!!!
//创建助教对象
Zhujiao zhujiao = new Zhujiao();
zhujiao.method();//方法执行!!!
}
}
继承访问成员变量
在父类的继承关系中,如果**成员变量重名**
,则创建子类对象时,访问有两种方式:
直接通过**子类对象访问成员变量**
:等号左边是谁就优先用谁,没有则向上找
间接通过**成员方法访问成员变量**
: 该方法属于谁,就优先用谁,没有则向上找
//创建父类
public class Fu {
int numFu = 10;
int num =100;
public void methodFu(){
System.out.println(num);
}
}
//创建子类
public class Zi extends Fu {
int numZi = 20;
int num =200;
public void methodZi(){
System.out.println(num);
}
}
public class DemoExtendsField {
public static void main(String[] args) {
Fu fu = new Fu();//创建父类对象
System.out.println(fu.numFu);//10 只能使用父类的东西,没有任何子类的内容
System.out.println("==============");
Zi zi = new Zi();
System.out.println(zi.numFu);//10
System.out.println(zi.numZi);//20
System.out.println("=================");
//等号左边是谁,就优先用谁
System.out.println(zi.num);//200
//这个方法是子类的,优先使用子类,没有在向上找
zi.methodZi();//200
//这个方法是在父类中定的,
zi.methodFu();//100
}
}
子类方法中重名的三种变量
局部变量: 直接写成变量名
本类的成员变量: this.成员变量名
父类的成员变量: super.成员变量名
继承三特点
super关键字
当子类和父类的属性/方法名重名时,可用**super**
关键字,子类**访问父类的方法和属性**
super.属性名//访问父类的属性
super.方法名(参数);//调用父类的方法。
super(参数);//调用父类的构造方法。
//初始化当前对象的父类型特征
super能出现在实例方法和构造方法中,不能使用在静态方法中
。且大部分情况下可以省略。
super()只能出现在构造方法第一行,通过当前的构造方法去调用、”父类”中其它的构造方法,目的是:代码复用
(创建子类对象的时候,先初始化父类型特征)。
public Dog(int age, String name,String strain) {
super(age, name);//super调用父类构造方法
this.strain=strain;
}
public void print(){
System.out.println(super.name+this.strain);//super调用父类属性
}
this关键字:
- 当成员变量与局部变量重名时,需要用
**this**关键字来声明成员变量
- this代表所在类的当前对象的引用(地址值),即对象自己的引用
方法被哪个对象调用,方法中的this就代表那个对象。即谁在调用,this就代表谁。
使用格式:**this.成员变量名;**
在本类的成员方法中,访问本类的成员变量
public class Test04 {
public static void main(String[] args) {
Test04 s = new Test04();
s.ShowNum();
}
int num = 20;
public void ShowNum(){
int num = 10;
System.out.println(num);//局部变量
System.out.println(this.num);//本类中的成员变量
}
}
在本类的成员方法中,访问本类的另一个成员方法 ```java public void methodA(){
System.out.println("a");
} public void methodB(){
this.methodA();
System.out.println("b");
- 在本类的构造方法中,访问本类的另一个构造方法,this(...)调用必须是构造方法第一个语句
```java
public Test04 (){
this(100);}//本类的无惨构造,调用本类的有参构造
public Test04(int n){}
重写父类方法:
- 方法名和参数必须完全一致
- 子类返回值
**类型**
必须**小于等于**
父类的返回值类型 - 子类方法的
**权限**
必须**大于等于**
父类的全新修饰符 - 父类的静态方法不能被子类重写
- 父类的私有方法不能被子类重写
重写父类规则:
A、重写规则之一:重写方法不能比被重写方法限制有更严格的访问级别。
B、重写规则之二:参数列表必须与被重写方法的相同。
C、重写规则之三:返回类型必须与被重写方法的返回类型相同。
D、重写规则之四:重写方法不能抛出新的异常或者比被重写方法声明的检查异常更广的检查异常。
E、重写规则之五:不能重写被标识为final的方法。
F、重写规则之六:如果一个方法不能被继承,则不能重写它。
多态只看父类引用的方法,而不看子类对象的方法
使用场景
当父类中的方法无法满足子类需求或子类具有特有功能的时候,需方法重写
重载和重写的区别:
- 方法重载是一个类的多态性表现,而方法重写是子类与父类的一种多态性表现。
重写:在继承关系中,子类重写父类方法,在不同类中,**方法名必须相同,参数表也需要相同,返回值与父类相同或是父类的子类**
,访问修饰符不能严于父类。
重载:在同一个类中,**方法名相同,形参列表不同**
,这种现象叫方法重载,方法重载与返回值和访问修饰符无关。
重写toString方法:方便打印对象值
重写equals方法:比较两个对象的值,默认是比较内存地址,重写以后比较的是值
继承中构造方法的访问特点
- 子类构造方法当中有一个默认隐含的
super()
调用,所以一定是先调用父类构造,后执行子类构造 - 子类构造可以通过super关键字来调用父类重载构造
- super的父类构造调用,必须是子类构造方法的第一个语句,不能一个子类调用多次super构造
总结:子类必须调用父类构造方法,不写则赠送super(),写了则用指定的super(),super只能是一个,而且是第一个
== 和 equals 的区别
当大家看了上面的内容,相信大家对这两种方式比较对象的区别应该有点了解
对于基本类型:
- == 比较的是具体的值
- equals 无法用于基本类型的比较,要注意
对于引用类型:
- == 比较的是地址值
equals 一般情况下比较的也是地址值,除非重写 equals 方法,自定义比较规则
访问修饰符
在java中,可以在类、类的属性以及类的方法前面加上一个修饰符,来对类进行一些访问上的控制
private
:私有的,限制在**本类**
中访问default
:什么都不写,就是default,**同类、同包**
protected
:受保护的,**同类、同包、不同包(限于子类访问)**
public
:公共的,**同类、同包、不同包**
final关键字
- 终结的,最终的,最后的
final可修饰的内容
- 类(最终类)
- 方法(最终方法)
- 变量(最终变量)
final修饰类:此类不能被继承。String、Math、System均为final修饰的类,不能被继承 final修饰方法:此方法不能被覆盖,意为最终方法,不支持子类以覆盖的形式修改 final修饰变量:此变量值不能被改变(常量),所以final修饰变量只能赋值一次,值不许改变
final修饰基本类型:**值不可变**
final修饰引用类型:**地址不可变**
**静态常量不在提供默认值,必须手动赋予初始值**
多态
- 是指同一行为,具有多个不同表现形式或形态能力,多态就是同一个接口,使用不同的实例而执行不同操作
多态体现的格式:
父类类型 变量名 = new 子类对象;
变量名.方法名();
父类类型:指子类对象继承的父类类型,或者实现的父接口类型
Fu f = new Zi();
f.method();
当使用多态方式调用方法时,首先检查父类中是否有该方法,如果没有,则编译错误;如果有,执行的是子类重写后方法
。
定义父类:
public abstract class Animal {
public abstract void eat();
}
定义子类:
class Cat extends Animal {
public void eat() {
System.out.println("吃鱼");
}
}
class Dog extends Animal {
public void eat() {
System.out.println("吃骨头");
}
}
定义测试类:
public class Test {
public static void main(String[] args) {
// 多态形式,创建对象
Animal a1 = new Cat();
// 调用的是 Cat 的 eat
a1.eat();
// 多态形式,创建对象
Animal a2 = new Dog();
// 调用的是 Dog 的 eat
a2.eat();
}
}
多态的好处
实际开发的过程中,父类类型作为方法形式参数,传递子类对象给方法,进行方法的调用,更能体现出多态的扩展性与便利
定义父类:
public abstract class Animal {
public abstract void eat();
}
定义子类:
class Cat extends Animal {
public void eat() {
System.out.println("吃鱼");
}
}
class Dog extends Animal {
public void eat() {
System.out.println("吃骨头");
}
}
定义测试类:
public class Test {
public static void main(String[] args) {
// 多态形式,创建对象
Cat c = new Cat();
Dog d = new Dog();
// 调用showCatEat
showCatEat(c);
// 调用showDogEat
showDogEat(d);
/*
以上两个方法, 均可以被showAnimalEat(Animal a)方法所替代
而执行效果一致
*/
showAnimalEat(c);
showAnimalEat(d);
}
public static void showCatEat (Cat c){
c.eat();
}
public static void showDogEat (Dog d){
d.eat();
}
public static void showAnimalEat (Animal a){
a.eat();
}
}
由于多态特性的支持,showAnimalEat方法的Animal类型,是Cat和Dog的父类类型,父类类型接收子类对象,当 然可以把Cat对象和Dog对象,传递给方法。
当eat方法执行时,多态规定,执行的是子类重写的方法,那么效果自然与showCatEat、showDogEat方法一致, 所以showAnimalEat完全可以替代以上两方法。
不仅仅是替代,在扩展性方面,无论之后再多的子类出现,我们都不需要编写showXxxEat方法了,直接使用 showAnimalEat都可以完成。
所以,多态的好处,体现在,可以使程序编写的更简单,并有良好的扩展。
引用类型转换
多态的转型分为**向上转型**
与**向下转型**
两种:
向上转型
多态本身是子类类型向父类类型向上转换的过程,这个过程是默认的。 当父类引用指向一个子类对象时,便是向上转型。
使用格式:
父类类型 变量名 = new 子类类型();
如:Animal a = new Cat();
对象的向上转型:**会失去子类新增**
子类对象,被看做了父类的类型,那么就不能访问子类的新增,只能访问父类的属性和方法,以及子类重写
向下转型
父类类型向子类类型向下转换的过程,这个过程是强制的。
使用格式:
子类类型 变量名 = (子类类型) 父类变量名;
如:Cat c =(Cat) a;
可以调用子类中特有的方法,可以调用父类中公开的方法
转换目标对象必须与转换类型一致,不然会报错
instanceof关键字
在java中,向下转型则是为了通过父类强制转为为子类,从而来调用子类独有方法,为了保证向下专项的顺利完成,在java中提供了一个关键字**instanceof**
,通过instanceof可以判断对象是否是某类的实例,如果是返回true,否则false
为什么要转型
当使用多态方式调用方法时,首先检查父类中是否有该方法,如果没有,则编译错误。也就是说,不能调用子类拥 有,而父类没有的方法。编译都错误,更别说运行了。这也是多态给我们带来的一点”小麻烦”。所以,想要调用子 类特有的方法,必须做向下转型。
定义类:
abstract class Animal {
abstract void eat();
}
class Cat extends Animal {
public void eat() {
System.out.println("吃鱼");
}
public void catchMouse() {
System.out.println("抓老鼠");
}
}
class Dog extends Animal {
public void eat() {
System.out.println("吃骨头");
}
public void watchHouse() {
System.out.println("看家");
}
}
定义测试类:
public class Test {
public static void main(String[] args) {
// 向上转型
Animal a = new Cat();
a.eat(); // 调用的是 Cat 的 eat
// 向下转型
Cat c = (Cat)a;
c.catchMouse(); // 调用的是 Cat 的 catchMouse
}
}
class A{
public void print(){
System.out.println("print:A");
}
}
class B extends A{
@Override
public void print(){
System.out.println("print:B");
}
public void fudB(){
System.out.println("fudB");
}
}
class C extends A{
@Override
public void print(){
System.out.println("print:C");
}
public void fudC(){
System.out.println("fudC");
}
}
public class Demo {
public static void test(A a){
a.print();
if(a instanceof B){
B b = (B)a;
b.fudB();
}
}
public static void main(String[] args) {
A a = new A();
B b = new B();
C c = new C();
test(a);
test(b);
test(c);
}
}
多态的两种体现形式:
- 父类作为返回值
父类作为返回值好处:return的时候可以return任意子类,就不固定某一种