封装
this关键字
- 在构造方法中出现this关键字,则代表当前正在构造的对象(经常用)
- 若在成员方法中出现this,则代表当前正在调用的对象
- this关键字可看作当前类类型的引用变量
- 当局部变量名和成员变量名相同时,方法体中会优先使用局部变量(就近原则),若希望使用成员变量,则需要在成员变量前加上this.前缀,明确要求改变量是成员变量(非常重要)
- this关键字除了可以通过this.的方式调用成员变量和成员方法外,还可以作为方法的返回值
- 在构造方法的第一行可使用this()的方式调用本类中的其他构造方法(了解)
形参就是局部变量,this区分的是成员变量
封装的概念
在测试类中可以给变量赋值一些合法但不合理的数值,这些数值不会报错。为了避免这样的错误,需要堆成员变量进行密封包装,隐藏成员变量的细节以及保证成员变量数值的合理性,这个机制就是封装。
属性私有,get/set
关键词:private
使成员变量只能在当前类的内部使用
并提供共有的get和set方法,在方法体中进行合理值得判断
把get和set方法设为public即可
idea中可用Alt+insert自动生成get和set
封装可避免有害数据破坏系统
set的用法
public void setAge(int age) {
if(age<0||age>100){
this.age = 0;
}else{
this.age = age;
}
}
get的用法
public void getAge() {
return age;
}
可以避免将岁数过大的数据输入到系统中。
最终用法
在共有的构造方法中调用set方法进行合理值的判断
封装的好处:
- 提高程序安全性,保护数据
- 隐藏代码实现细节
- 统一接口
- 系统可维护性增加了
编程实现学生信息的录入和打印
- 根据用户输入的学生人数用变量来记录
- 根据学生人数准备一维数组
- 根据用户输入的每个学生信息(学号,姓名)记录到一维数组中
- 打印学生信息
主程序代码
import java.util.Scanner;
public class StudentTest2 {
public static void main(String[] args) {
System.out.println("请输入学生人数:");
Scanner sc = new Scanner(System.in);
int num = sc.nextInt();
//数组中每个元素都可以看作Student类型的变量
//Student[0] = new Student();
Student[] arr = new Student[num];
for (int i = 0; i < num; i++) {
System.out.println("请输入第"+(i+1)+"个学生的信息(学号 姓名)");
arr[i] = new Student(sc.nextInt(),sc.next());
}
System.out.println("-------------------------------");
for (int i = 0; i < num; i++) {
//System.out.println(arr[i]);会打印地址信息
arr[i].show();
}
}
}
Student方法中代码
public class Student {
private int id;
private String name;
public Student(){
}
public Student(int id,String name){
setName(name);
setId(id);
}
public int getId() {
return id;
}
public void setId(int id) {
if(id>0) {
this.id = id;
}else{
System.out.println("输入了不合理的学号值");
}
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public void show(){
System.out.println("我的学号是:"+getId()+"\t我的姓名是:"+getName());
}
}
结果是:
static关键字
加了static关键字后成员变量可被所有对象共享,隶属于类层级
static也可修饰成员方法,而在静态方法中没有this关键字,静态方法可直接用类名调用
因为this代表的是调用这个函数的对象的引用,而静态方法是属于类的,不属于对象,静态方法成功加载后,对象还不一定存在
封装的案例(重中之重)
- 编程实现Singleton类的封装
- 编程实现SingletonTest对Singleton类进行测试,要求能得到且只能得到该类的一个对象
核心思想是让构造方法不能容易的被调用
Singleton s1 = new Singleton();
Singleton s2 = new Singleton();
这就是对构造方法没有限制,可创建多个对象
正确方法如下:
单例模式
继承
extends扩展,子类可以继承父类的所有方法
有父类的全部方法,前提是public
所有类都直接或间接继承object类
super的运用
super();
表示调用父类的构造方法,只能写在首行
括号内加参数就代表调用了有参构造
的输出是:
Xiaoming
Xiaohong
Apple
但私有的东西private不能被继承。
而且在创建子类的对象时,会默认先调用父类的无参构造
方法的重写
重写都是方法的重写,和属性无关
只有非静态的方法才有重写(无static),且关键词只能是public
重写需要有继承关系,子类重写父类的方法,方法体不同(Alt+insert+override)
- 方法名必须相同
- 参数列表必须相同
- 修饰符范围可以扩大不能缩小:public>Protected>Default>private
- 抛出的异常:范围可以被缩小不能扩大
为什么需要重写?
父类的功能子类不一定需要或不一定满足
重写的重要意义在于如果重写了就调用子类的,没有重写就调用父类的
继承的案例
public class Animal {
private String name;
private String color;
public Animal() {
}
public Animal(String name, String color) {
this.name = name;
this.color = color;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
}
public void show(){
System.out.println("名字是:"+getName()+"\t毛色是:"+getColor());
}
}
public class Dog extends Animal{
private int tooth;
public Dog() {
}
public Dog(String name, String color, int tooth) {
super(name, color);//表示调用父类的构造方法
this.tooth = tooth;
}
public int getTooth() {
return tooth;
}
public void setTooth(int tooth) {
if(tooth>0&&tooth<100){
this.tooth = tooth;}
else{
System.out.println("输入的牙齿数量不合理");
}
}
@Override
public void show() {
super.show();
System.out.println("牙齿数量是:"+getTooth());
}
}
public class DogTest {
public static void main(String[] args) {
Dog d1 = new Dog();
d1.show();
Dog d2 = new Dog("旺财","红色",30);
d2.show();
}
}
结果
构造块和静态代码块的考点
构造块即类中用大括号括起来的代码块
执行之前会先加载方法区中的静态代码块
而执行构造方法前会先执行构造代码块
public class SuperTest {
{
System.out.println("SuperTest中的构造块!");
}
static{
System.out.println("SuperTest中的静态代码块");
}
public SuperTest(){
System.out.println("SuperTest中的构造方法体");
}
public static void main(String[] args) {
SuperTest st = new SuperTest();
}
}
结果是
这时有SuperTest的子类SubSuperTest
public class SubSuperTest extends SuperTest{
{
System.out.println("==========SuperTest中的构造块!");
}
static{
System.out.println("==========SuperTest中的静态代码块");
}
public SubSuperTest(){
System.out.println("===========SuperTest中的构造方法体");
}
public static void main(String[] args) {
SubSuperTest st = new SubSuperTest();
}
}
问执行SubSuperTest时候的加载次序
- 首先肯定会先加载父类,则父类中的静态代码块会先执行
- 然后就会加载子类中的静态代码块
- 父类的构造方法会先于子类的构造方法,所以第三步加载父类的构造快
- 然后加载父类的构造方法,才算父类对象创建完成
- 执行子类构造快
- 执行子类构造方法
结果如下:
常用的控制访问符
final
final可修饰类、成员方法和成员变量
final关键字修饰的类不能被继承
final关键字修饰的方法不能被重写但可以被继承
多态
一个对象的实际类型是确定的,可以指向的引用类型不确定
例子
public class Shape {
private int x;
private int y;
public Shape(){
}
public Shape(int x,int y){
setX(x);
setY(y);
}
public int getX() {
return x;
}
public void setX(int x) {
this.x = x;
}
public int getY() {
return y;
}
public void setY(int y) {
this.y = y;
}
void showX(){
System.out.println("横坐标是:"+getX()+"\t纵坐标是:"+getY());
}
}
public class Rect extends Shape{
private int length;
private int width;
Rect(){
}
public Rect(int x,int y,int length, int width) {
super(x,y);
this.length = length;
this.width = width;
}
public int getLength() {
return length;
}
public void setLength(int length) {
this.length = length;
}
public int getWidth() {
return width;
}
public void setWidth(int width) {
this.width = width;
}
}
public class ShapeRectTest {
public static void main(String[] args) {
//1.声音Shape类型引用指向Shape类型对象
Shape s1 = new Shape(1,2);
s1.showX();
System.out.println("====================");
//2.声明Rect类型引用指向Rect类型对象
Rect s2 = new Rect(3,4,5,6);
s2.showX();
System.out.println("====================");
//3.声明Shape类型引用指向Rect类型对象
Shape s3 = new Rect(7,8,9,10);
s3.showX();
}
}
结果是
说明了当Rect没有重写Shape中的show()时,就只会调用Shape中的show()
当Rect中重写了show方法后
说明当父类引用指向子类时,调用的是子类重写后的方法
但是在编译阶段调用的是父类中的show方法,运行阶段调用子类的show方法
且Shape类的引用也可以调用父类中独有的方法
自定义成员方法实现将参数指定矩形对象特征打印出来,也就是绘制图像的行为
public class ShapeTest {
//自定义成员方法实现将参数指定矩形对象特征打印出来,也就是绘制图像的行为
public static void draw(Rect r){
r.showX();
}
public static void main(String[] args) {
//Rect r = new Rect(1,2,3,4)
ShapeTest.draw(new Rect(1,2,3,4));
}
}
一个类的实际对象是确定的,都是new Student(),但是指向的引用类型不一样。
用Student重写了Person的run方法,这里面:
- Student能调用自己的和父类的方法
- Person只能调用自己的方法,只有指向子类,但不能调用子类独有的方法
static、final、private不能被重写
instanceof和类型转换
instanceof关键字可用来测试两个类之间是否存在父子关系。
由注释中关系理解结果
强制转换:父类Person ——>子类Student
Person obj = new Student();
((Student)obj).go();
这样就把Person类obj转换为Student类,可使用Student类中的go方法
而子类转换为父类可能会丢失自己本来的一些方法
回顾一下:
多态就是父类引用指向子类对象
Person obj = new Student();
抽象类
抽象方法是只有方法的名字,没有方法的实现,如:
public abstract void doSomething();
抽象类的所有方法,继承了它的子类,都必须实现它的方法
抽象类不能new出来,只能靠子类去实现它。相当于约束
抽象类中也可以写普通方法
接口可以实现多继承!
接口
接口只有规范,自己无法写方法。约束和实现分离。接口的本质是契约
接口中所有定义都是抽象的public abstract
所有接口都要有实现类 implement关键字
利用接口实现多继承如下
接口中没有构造方法