一、封装
理论
先看下面的代码
public class Test{
public static void main(String[] args){
User u = new User();
u.age = -100;
u.age = -400;
}
}
class User{
int age;
}
对于当前程序来说:User
类中的age
属性在外部程序中可以随意访问,导致age
属性的不安全。
一个User
对象表示一个用户,用户的年龄不可能为负数,上面程序当中年龄值为负数,程序运行的时候并没有报错,这是当前程序存在的缺陷。所以需要封装机制。
对于封装而言,一个对象它所封装的是自己的属性和方法。封装的的好处:
- 封装之后,对于那个事物来说,看不到这个事物比较复杂的那一面,只能看到该事物简单的那一面。复杂性封装,对外提供简单的操作入口。
照相机就是一个很好的封装的案例,照相机的实现原理非常复杂,但是对于使用照相机的人来说,操作起来是非常方便的是非常便捷的。还有像电视机也是封装的,电视机内存实现非常复杂,但是对于使用者来说不需要关心内部的实现原理,只需要会操作遥控器就行。- 封装之后才会形成真正的“对象”,真正的“独立体”
- 封装就意味着以后的程序可以重复使用。并且这个事物应该适应性比较强,在任何场合都可以使用。
- 封装之后,对于事物本身,提高了安全性。【安全级别高】
封装把一个对象的属性私有化,同时提供一些可以被外界访问的属性的方法
如果不想被外界方法,我们大可不必提供方法给外界访问。
步骤
- 所有属性私有化,使用
private
关键字进行修饰,private
表示私有的,修饰的所有数据只能在本类中访问。 - 对外提供简单的操作入口,也就是说以后外部程序要想访问
age
属性,必须通过这些简单的入口进行访问 - 对外提供两个公开的方法,即
set、get方法
- 想修改age属性,调用set方法
- 想读取age属性,调用get方法
- set方法的命名规范:
-
public void setAge(int a) {age = a;}
-
- get方法的命名规范:
-
public int getAge (){return age;}
-
注意:
set、get方法
没有static
关键字
有static
关键字修饰的方法调用:类名.方法名(实参) ;
没有static
关键字修饰的方法调用:先创建对象和引用、引用.方法名(实参) ;
public class Test{
public static void main(String[] args){
User u = new User();
//报错。因为私有化属性不可以直接访问
System.out.println(u.age);
//通过set、get方法修改和访问
u.setAge( -100 );
System.out.println( u.getAge );
}
}
class User{
//属性私有化
private int age;
//set方法,没有返回值,只负责修改数据
pubilc void setAge(int a){
age = a;
}
//get方法
public int getAge(){
return age;
}
}
操作入口变成了 set、get 方法
,在set、get 方法
执行过程中,进行安全过滤
可以设定修改数值的条件
class User{
//属性私有化
private int age;
//set方法
pubilc void setAge(int a){
if( a < 0 || a > 150){
System.out.println("您输入的年龄有误");
return;
}
age = a;
}
//get方法
public int getAge(){
return age;
}
}
class person{
private String sexName;
public String getSexName() {
if("0".equals(sex)){
sexName = "女";
}else if("1".equals(sex)){
sexName = "男";
}else{
sexName = "人妖";
}
return sexName;
}
}
二、继承
继承
语法格式:[修饰符列表] class 类名 extends 父类名{ 类体; }
- Java中是单继承,一个类只可以继承一个类
- B类 extends A类:
- B类:子类、派生类、
subclass
- A类:父类、基类、超类、
surperclass
- B类:子类、派生类、
- 私有的不可以继承,构造方法不可以继承。其他均可以继承
- 虽然只有单继承,但是可以间接继承
- 若一个类没有显示
extends
某个类,则默认继承Object类
。
java中任何一个类都有Object类
特征
这里的tt.toString()
方法就是Object类
中继承过来使用的
public class Test{
public static void main(String[] args){
Test tt = new Test();
String s = tt.toString();
System.out.println(s);
}
}
编写子类父类
public class Test{
public static void main(String[] args){
Animal a = new Animal();
a.move();
Cat c = new Cat();
c.move();
Bird b = new Bird();
b.move();
}
}
class Animal{
public void move(){
System.out.println("动物行走");
}
}
class Cat extends Animal{}
class Bird extends Animal{}
这里的set / get方法
不是私有的,所以子类可以调用父类方法
public class Test{
public static void main(String[] args){
B bb = new B();
bb.setAge(20);
bb.setName("草莓");
System.out.println(bb.getAge+","+bb.getName);
}
}
class A{
private int age;
String name;
public A(){}
public A(int age,String name){
this.age = age;
this.name = name;
}
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;}
}
class B extends A{
private int id;
public B(){
super();
}
public int getId(){return id;}
public void setId(int id){this.id = id;}
}
方法覆盖/重写 Override
方法重载 Overloard
- 什么么情况下使用?
当同一个类中,方法功能是相似的,建议使用方法重载 - 需要满足什么条件?
在同一个类中,方法名相同,参数列表不同:类型、顺序、数量 - 方法重载和什么无关?
和方法返回值、方法的修饰符列表无关
方法覆盖 Override
- 什么情况使用?
当父类中的方法无法满足子类的需求,子类需要将父类的方法进行升级改造 - 子类重写方法的
方法名、形参列表
和父类被重写方法一致 - 子类重写方法的
权限修饰符
大于等于父类被重写方法 - 子类不能重写父类中
private
修饰的方法 - 返回值类型
public class Test{
public static void main(String[] args){
Animal a = new Animal();
a.move();
Cat c = new Cat();
c.move();
Bird b = new Bird();
b.move();
}
}
class Animal{
//假设这边还有其他一百多行代码
public void move(){
System.out.println("动物行走");
}
}
class Cat extends Animal{
public void move(){
System.out.println("猫走猫步");
}
}
class Bird extends Animal{
public void move(){
System.out.println("鸟在飞");
}
}
三、多态
多态涉及到的两个概念:
- 向上转型
子类(多) ====> 父类(少) (自动型) - 向下转型
父类(少) ====> 子类(多) (强制型,需要加强制类型转换符)
**注意:**无论是向上还是向下,两个类型之间要有继承关系,没有继承关系是无法转型的
向上转型
【父类引用指向子类对象】
//父类,动物类
class Animal {
protected void run() {
System.out.println("动物在移动!!!");
}
}
//子类,猫类
class Cat extends Animal {
//重写父类方法
public void run() {
System.out.println("猫在走猫步!!!");
}
//子类特有的方法,猫会抓老鼠
public void catchMouse() {
System.out.println("猫正在抓老鼠!!!");
}
}
//子类,鸟儿类
class Bird extends Animal {
//重写父类方法
public void run() {
System.out.println("鸟儿在飞翔!!!");
}
//子类特有的方法,鸟正在飞
public void fly() {
System.out.println("鸟正在飞!!!");
}
}
使用多态语法机制
new Cat()
创建的对象是Cat
类型,子类类型,a1
这个引用是Animal
类型,父类类型
子类类型转换为父类类型,称为自动类型转换,向上转型 / upcasting
父类引用指向子类对象
public class Test{
public static void main(String[] args){
//使用多态语法机制
Animal a1 = new Cat();
a1.run();
}
}
输出结果:猫在走猫步!!!
理论:
java程序分为编译阶段和运行阶段
先编译,再运行
编译阶段:编译器知道a1
这个引用是Animal
类型,在编译检查a1.run()
的语法时,会去引用a1
的Animal.class
中寻找run()
方法,成功找到,编译通过。
该阶段称为 “ 静态绑定 ” ,“ 编译阶段绑定 ”
运行阶段:JVM 堆内存当中真是创建的对象是Cat对象
,程序在执行阶段a1.run()
调用的是Cat对象
内部的run()
方法
该阶段称为 “ 动态绑定 ” ,“ 运行阶段绑定 ”
无论子类是否重写父类方法,运行阶段调用的一定是子类中的方法。就算未重写,也已经继承了
注意以下代码: 程序为什么不通过?
编译阶段:引用a1
的数据类型是Animal
类型,在Animal.class
中寻找不到catchMouse()
方法,所以编译不通过
//编译错误
public class Test{
public static void main(String[] args){
Animal a1 = new Cat();
a1.catchMouse();
}
}
程序为什么不通过?
因为二者之间没有继承关系,无法进行转换
Bird b = new Cat();
向下转型
向上转型可以调用子类中和父类同名的方法。当调用的方法是子类中特有的,在父类当中不存在时,就需要向下转型、
语法结构:子类类型 新引用 = (子类类型) 父类引用;
a1
是无法直接调用catchMouse()
方法的,因为Animal.class
中没有该方法
public class Test{
public static void main(String[] args){
Animal a1 = new Cat();
a1.run();
Cat c1 = (Cat)a1;
c1.catchMouse();
}
}
注意以下代码: 程序为什么不通过?
编译通过,因为a2
的数据类型是Animal
类型,与Cat
类型之间存在继承关系,可以转型。c2
的数据类型是Cat
类型,对象中存在catchMouse()
方法,静态绑定成功,编译通过。
运行不通过。报错:ClassCastException
类型转换异常
因为JVM堆内存中真实存储的对象是Bird
类型,Bird
对象无法转换成Cat
对象,因为二者之间不存在继承关系
//运行报错
Animal a2 = new Bird();
Cat c2 = (Cat)a2;
c2.catchMouse();
instanceof运算符
为了避免上面的异常发生,就有了instanceof
运算符
语法格式:引用 instanceof 数据类型名
即判断前面的引用指向的对象是不是后面的数据类型
所以上面的代码应该写成如下:
public class Test{
public static void main(String[] args){
Animal a2 = new Bird();
a2.run();
if(a2 instanceof Cat){
Cat c2 = (Cat)a2;
c2.catchMouse();
}else if(a2 instanceof Bird){
Bird b2 = (Bird)a2;
b2.fly();
}
}
}
例题理解多态的作用
注意: 方法传递数据的时候传递的是具体的值,所以下面例题中,调用主任方法时,传递的是具体对象
pipi.feed(new Cat());
下面给道例题理解一下多态在开发中的作用:
/*
* 编写程序模拟主人喂养宠物的场景:
* 提示一:
* 主人类:Master
* 宠物类:Pet
* 宠物子类:Dog,Cat,Bird
* 提示二:
* 主人应该有喂养方法:feed()
* 宠物应该有吃的方法:eat()
* 只要主人喂养,宠物就吃。
*
* 要求:主人类中只提供一个喂养方法feed(),要求达到可以喂养各种类型的宠物。
* 编写测试程序:创建主人对象
* 创建各种宠物对象
* 调用主人的喂养方法feed(),喂养不同的宠物,观察执行结果。
* 通过改案例,理解多态在开发中的作用。
* 重要提示:feed()方法是否需要一个参数,参数选什么类型???
*/
public class MyTest{
public static void main(String[] args) {
//实例化主人类
Master pipi = new Master();
//实例化三种宠物类
Dog d1 = new Dog();
Cat c1 = new Cat();
Bird b1 = new Bird();
//主人调用自己的喂养方法feed()来实现不同宠物的吃方法eat()
pipi.feed(d1);
pipi.feed(c1);
pipi.feed(b1);
}
}
//主人类
class Master {
//feed()方法
public void feed(Pet pet) {
pet.eat();
}
}
//宠物类
class Pet {
//eat()方法
public void eat() {
System.out.println("宠物" + "在吃东西!!!");
}
}
//宠物子类,Dog类
class Dog extends Pet {
//重写eat()方法
public void eat() {
System.out.println("狗狗" + "喜欢啃骨头!!!");
}
}
//宠物子类,Cat类
class Cat extends Pet {
//重写eat()方法
public void eat() {
System.out.println("猫儿" + "喜欢吃鱼!!!");
}
}
//宠物子类,Bird类
class Bird extends Pet {
//重写eat()方法
public void eat() {
System.out.println("鸟儿" + "喜欢吃虫子!!!");
}
}