前提提要:代码块中均为伪代码,不是正式规格代码,只是为了方便大家观看方便易解,有错误请指正
///【封装】/
简而言之:1、方法
2、private的变量配合保护的方法
其实就是方法,就是一堆很麻烦的事情、流程装在一个“花括号”方法里,也就是函数,要干什么只需要调用一下这个方法就行,要有什么要求更改,只需要在封装好的方法里改一下就行
不需要理解那么多原理,就是【方法】
举例:比如设置一个【人】类,这个类里有个属性【age】
至于private是干什么? 就是这个在类里写好的成员变量,不希望在类外面给它们赋值时被随意乱设置,就用private保护不让在【类外面】直接赋值,只能通过【间接的方式】按照要求来给正确的值。
如果直接在类里:int age; 然后在类外面赋值:人.age = -20;这明显就是错的,因为人的年龄不能是负数啊!
必须要设置一个private不让外面直接赋予它乱值:private int age;
然后再设置一个间接赋值的方法:
public void setAge(int age){
if(age>=0){
this.age = age;
}else{
System.out.println("输入错误数据!");
}
}
这样一来【人】这个类的age才能赋予正常的数值
然后创建类通常就两个方法搭配:
1、设置值:
public void set变量(参数){
this.变量 = 参数;
}
2、获得值:
public 变量数据类型 get变量(){
return this.变量;
}
(拓展:构造方法)
在创造一个【类】的时候,哪怕什么也没有,编译器也会给一个什么内容、作用、参数都没有的【无参构造方法】
何为【构造方法】?就是:1、方法名跟类名一模一样
2、什么返回值也没有,连void也没有
例子:
创建了一个【people】类:
public class people{
//啥也没有
}
此时编译器会赠送一个【无参构造方法】:
public class people{
public people(){}
}
但是如果一旦自己手动设置了一个【构造方法】,编译器就不会赠送【无参构造方法】
例子:
手动设置一个【无参构造方法】和【带参构造方法】:
public class people{
private int age;
private String name;
public people(){ ——————> 【无参构造方法】
//啥也没有
}
public people(String name,int age){ ——————> 【带参构造方法】
this.name = name;
this.age = age; ——————> 同时有参构造方法是无参构造方法的【重载】,
【同方法名】、【不同的参数列表】
}
}
///
/【继承—>抽象】///
三大类:【普通类】、【继承类】、【抽象类】
【继承类】(可以继承【普通类】和【抽象类】)
概念理解:
就是一开始我们设置的【父类】就相当于一个【老款手机】,它有基础的【打电话、发短信、屏幕显示号码】功能,随着时代发展更新换代,【老款手机】淘汰取而代之的是【新款手机】
但是【新款手机】只是在【继承】【老款手机】的基础功能的同时,更新加入了新的【显示号码联系人、联系人头像】这些功能,那我们要做的只是在新手机上去继承老手机的系统功能,
然后稍加改动,而【不是】向所有老手机用户强制召回【老手机】,然后把【老手机】翻新修改完再卖回给客户,这很傻逼;那么java继承就是这么个道理。
1、先测试【子类】能否直接继承【父类】的【方法】
例子:
//这是一个【父类】
public class Animal {
public void method(){
System.out.println("我是动物!!!");//父类里面有一个方法,试试其他继承类
//能不能继承他这个方法
}
}
//这是两个【子类】
public class 继承Animal的dog extends Animal{
//现在写的这个【dog继承类】用【extends】跟【Aniaml】这个父类【连接】上了关系
//即使什么都不写,它里面也含有【Animal类】里的【method方法】
}
public class 继承Animal的rabbit extends Animal{
//现在写的这个【rabbit继承类】用【extends】跟【Aniaml】这个父类【连接】上了关系
//即使什么都不写,它里面也含有【Animal类】里的【method方法】
}
//这是一个【测试类】
public class test_各个继承类 {
public static void main(String[] args){
继承Animal的dog dog = new 继承Animal的dog();//这里定义了一个继承类dog的对象
dog.method(); ——————> 输出"我是动物!!!"
//直接调用dog对象的方法,虽然dog继承类里啥也没有,但其实调用了总的Aniaml的方法
继承Animal的rabbit rabbit = new 继承Animal的rabbit();
//这里定义了一个继承类rabbit的对象
rabbit.method(); ——————> 输出"我是动物!!!"
//直接调用rabbit对象的方法,虽然rabbit继承类里啥也没有,但其实调用了总的Aniaml的方法
}
}
2、测试【子类】能否直接继承【父类】的【成员变量】
例子:
//这是一个【父类】
public class Father {
static String name = "Father";//这是一个跟子类【重名】的变量,这里的name属于父类
static int num = 100;//这个是只有父类有的变量,我在子类里没有定义num这个变量
//如果没有static设置静态变量,就没法在static的main方法里用这个变量
//【补充知识】:static修饰的变量叫静态变量,属于【类变量】,不需要创建【对象】就可以直接访问:类.变量
// 而没有static修饰的变量叫非静态变量,属于【对象变量】,必须创建了实例对象才可以访问:对象.变量
// 还有,static修饰的方法叫静态方法,静态方法里只能用静态变量不能用非静态方法
}
//这是一个【子类】
public class Son extends Father{
String name = "son";//这是一个跟父类【重名】的变量,这里的name属于子类
double hahaha = 3.1415926;
//注意这里只有一个String类的name变量和double类的hahaha变量,没有别的变量,
//Father父类里除了【重名】的name变量还有一个int类的num变量,但是父类里我没有设double类的
//hahaha变量
}
//这是一个【测试类】
public class test_各个继承类 {
public static void main(String[] args){
Son son = new Son();
//这里虽然Son类里没有设置num变量,但是他继承了Father父类,因此可访问父类里的num
System.out.println(son.num); ——————> 输出"100"
//而这里Son和Father两个类都有name变量,重名了,那么调用谁就优先选谁
System.out.println(son.name); ——————> 输出"son"
System.out.println(Father.name); ——————> 输出"Father"
//System.out.println(Father.hahaha);这样就报错,为什么?
//因为hahaha这个变量是son类的,只有儿子继承父亲的性质,父亲认不出儿子的性质
}
}
3、测试【子类】间接在访问【自己方法的局部同名变量】、【本类同名变量】、【父类同名变量】
例子:
//这是一个【父类】
public class Father {
static String name = "Father";//这是一个跟子类【重名】的变量,这里的name属于父类
}
//这是一个【子类】
public class Son extends Father{
String name = "son";//这是一个跟父类【重名】的变量,这里的name属于子类
public void method(){
String name = "grandson";//在子类的成员方法里又来一个重名变量name,现在包括父类在内一共三个重名变量,咋区分?
System.out.println();
System.out.println(name);//那么局部变量直接用原名 ——————> 输出"grandson"
System.out.println(this.name);//本类变量用this ——————> 输出"son"
System.out.println(super.name);//父类变量用super ——————> 输出"Father"
}
}
//这是一个【测试类】
public class test_各个继承类 {
public static void main(String[] args){
Son son = new Son();
son.method(); ——————> 输出"grandson"、"son"、"Father"
//子类方法里有三个重名变量name,都正常输出了,都是不同的
}
}
搞定了继承初步理解,也就是明白儿子可以怎么继承一些父亲的特征,那么就要理解【重写方法】,你光继承了父亲的特征不能一模一样啊,得有改进,那就通过【重写方法改进】
注意事项:
【第1个】注意事项:跟【方法重载】类似,
但是重载是【方法名相同】【参数列表相同】
而重写是【方法名相同】【参数列表相同】
错误例子:
public String "Call" ("String s"){ ————> 1、父类的call方法的名字是call而不是Call
return"打电话"; 2、父类的call方法参数列表里没有传参,而这里传了一个String类的s
} ————> 因此这个方法不会报错,但是它跟父类的call方法【没有半毛钱关系】,不是重写
为了防止我们这些傻逼写完了还不知道错了,检查我们的方法是不是正确【重写方法】,可以加一个【@Override】
例子:
@Override
public String call(){
return "打电话"; ————> 没有被@Override检查出错误,说明是正确重写方法
}
【第2个】注意事项:“子类”重写方法的【权限】必须必须【大于等于】“父类”的【权限】 【补充知识】
【权限大小关系】:public > protected > (default,也就是什么也不写) > private
错误例子:
@Override
protected String call(){ ————> 父类的call方法权限是public,而子类的权限确实protected,小于父类权限错了
return "打电话";
}
【第3个】注意事项:“子类”重写方法的【返回值类型】必须必须【小于等于】“父类”的【返回值类型】】
【补充知识】
【返回值类型关系】:Object(java.lang.Object)食物链最顶端,最大的返回值类型,其他暂时不知
错误例子:
@Override
public "Object" call(){ ————> 父类的call返回值类型是String,而子类的返回值类型却是Object,大于父类权限错了
return "打电话";
}
1、测试【子类】重写【父类】方法
例子:
//这是一个【父类】
public class 父类Phone {
public String call(){
return "打电话";
}
public String send(){
return "发短信";
}
public String show(){
return "显示号码";
}
}
//这是一个【子类】
public class 子类phone extends 父类Phone{
@Override
public String show(){
//现在要重写改进【父类老phone的show方法】让它不仅只显示号码,还要显示联系人姓名、联系人头像
//那么老方法里已经有了“显示号码”,不需要再重复在【重写方法】里再去写它,只需要【提到父类老方法】里有
//然后再加上新的内容即可
String s = super.show() + " 显示联系人 显示联系人头像"; ————> super就是提到【父类老方法】里的东西
return s;
}
}
//这是一个【测试类】
public class test_各个继承类 {
public static void main(String[] args){
父类Phone OldPhone = new 父类Phone();
System.out.println(OldPhone.call()); ——————> 输出"打电话"
System.out.println(OldPhone.send()); ——————> 输出"发短信"
System.out.println(OldPhone.show()); ——————> 输出"显示号码"
System.out.println("---------------------------");
子类phone NewPhone = new 子类phone();
System.out.println(NewPhone.call()); ——————> 输出"打电话"
System.out.println(NewPhone.send()); ——————> 输出"发短信"
System.out.println(NewPhone.show()); ——————> 输出"显示号码 显示联系人 显示联系人头像"
}
}
2、测试【子类】自动调用和手动调用【父类】的【构造方法】
例子:
//这是一个【父类】
public class 父类Phone {
//这是一个父类【无参】构造方法
public 父类Phone(){
System.out.println("父类【无参】构造函数方法被调用");
}
//这是一个父类【有参】构造方法
public 父类Phone(String s){
System.out.println(s);
}
}
//这是一个【子类】
public class 子类phone extends 父类Phone{
//这是一个子类构造方法
public 子类phone(){
//super();这个方法是隐含的,是编译器默认赠送的,所以子类构造方法一旦被调用,就必先调用父类无参构造方法,再到子类构造
//但是注意!自带隐含的是【无参的父类构造方法】
//如果要调用父类的【有参构造方法】,还是跟访问父类变量的道理一样:super(参数);
super("父类【有参】构造方法被调用");//还有注意:1、这个语句必须写在第一步!!!
2、一个子类不可以调用多个父类构造方法
System.out.println("子类【无参】构造函数方法调用");
}
}
//这是一个【测试类】
public class test_各个继承类 {
public static void main(String[] args){
//父类Phone OldPhone = new 父类Phone(); ——————> 输出"父类【无参】构造函数方法被调用"
子类phone NewPhone = new 子类phone();
//(没注释父类对象时)——————> 输出"父类【无参】构造函数方法被调用"
//(注释父类对象时) ——————> 输出"父类【有参】构造函数方法被调用"
输出"子类【无参】构造函数方法调用"
//前面学了,当创建了一个实例对象时,就是调用了构造方法,这里子类里我设置了子类的构造方法,会被调用
//但是很神奇发现,就算没有父类对象,子类对象也能把父类的构造方法调用出来
//后面我手动添加了一个【父类有参】构造方法,父类的【无参】构造方法就不会被赠送给【子类】,取而代之的是父类【有参】
}
}
引入一个知识点【final】关键字---------------------------------------------------------------------------------------
final就是把”变量“(可以变的数据变成不能变)变成”常量“,这里变量常量不只是指变量,还包括(类、方法、变量)
1、【final】修饰的【类】不给【继承】!
就给【父类】”结扎“了,断子绝孙(至于有什么用,我也不知道,神经病)
例子:
//这是一个父类
final public class 随便一个类{
private String name;
private int age;
public 随便一个类(){
}
public 随便一个类(String name,int age){
this.name = name;
this.age = age;
}
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 想继承的类 extends 随便一个类{
// ......
//}
//这里报错,因为【随便一个类】我已经用final修饰了,不给继承,断子绝孙
2、【final】修饰的【方法】不给【重写】!
例子:
//这是一个父类
public class father{
final public void method(){
System.out.println("只给父类使用这个方法,不让子类用");
}
}
//这是一个子类,继承父类
public class son extends father{
// 这里父类里用final修饰了这个方法,不让子类重写用
// @Override
// public void method(){
// System.out.println("子类偏就想用,但是用不了");
// }
}
3、【final】修饰的【变量】变成【常量】
确切的说是:【final】修饰的【数据】不能变,因为
(1)final修饰基本数据类型时,数据值不让变
(2)final修饰的引用数据类型时,数据就成了地址,地址不让边,而里面值可以变
例子1(final修饰基本数据类型):
public class 测试final修饰关键字 {
public static void main(String[] args){
final double PI = 3.1415926;
//PI = 20;这样就会错,因为final已经把PI变成常量了,不能再改它的值
}
}
例子2(final修饰引用数据类型):
//这里随便定义一个类
final public class 随便一个类{......}
public class 测试final修饰关键字 {
public static void main(String[] args){
final int[] b = {1,2,3,4,5};
b[0] = 100;
b[1] = 200;
// 这样却不会错,为什么?因为final修饰的引用数据类型是把【地址】变为常量,而值还是可以改变的
// b = new int[5];现在这样就错了,地址不能变,所以不可以让b又改到一个新数组的地址
final 随便一个类 c = new 随便一个类("Mike",12);
c.setName("Stupid dog");
c.setAge(100);
System.out.println(c.getName()+" "+c.getAge());
// 一样,这样却不会错,因为final修饰的引用数据类型是把【地址】变为常量,而值还是可以改变的
// 随便一个类 c = new 随便一个类();现在这样就错了,地址不能变,所以不可以让对象c又改到一个新对象的地址
}
}
---------------------------------------------------------------------------------------------------------------------------------
【抽象类和抽象方法】
理解:【抽象类】即相当于创建【父类】,可以作为继承类的父类,比如"动物",什么动物?牛?羊?不知道
【抽象方法】就是大概的“动作”,比如“吃”,具体怎么吃?吃什么?不知道
那为什么要有抽象类?有一个大概的【父类】,然后子类正常继承,然后再改不就行了?
比如建立”动物父类“,”狗子类“和”猫子类“,然后父类里的”吃方法“的方法体写个大概:【吃东西】,然后【”狗“子类】的”吃方法“重写【吃屎】,【”猫“子类】的”吃方法“写【吃鱼】,搞定
但是这里就出了一个弊端:
1、万一其中一个子类忘了重写父类方法,那就会【直接继承】父类里的【原方法】,比如【”狗”子类】里没有写方法,那它也会获得父类的方法里的【吃东西】,可是它要吃屎啊!没屎怎么活?
那么抽象类就可以治这种情况,子类忘了写是吧?没得继承!!因为抽象父类里的抽象方法没有【方法体】!
2、又或者要是【父类】里没写【吃东西】的抽象方法,然后【子类】里的【吃东西方法】一会叫eat一会叫eating一会叫Eat,那无数个类有无数个【吃东西】方法,别人接你这个项目就很想死,眼花缭乱
【所以总结:抽象类和抽象方法就是【强制要求】子类必须【规范】继承使用其中一种方法】
【抽象类】
注意事项:1、抽象类不可以直接创建对象。理解:你创建一个动物怎么创建?是狗?还是猫?还是鱼?
2、必须依靠一个子类继承抽象父类,然后才可以创建子类对象使用
3、子类必须【重写】【所有】抽象父类里的【抽象方法】(除非这个子类也是一个【抽象类】)
【格式】
在【class】前面加一个abstract就可以
public abstract class Animal{
}
子类继承不需要写abstract,正常extends继承就行
【抽象方法】
注意事项:1、抽象类可以没有抽象方法,但是【抽象方法】【一定】得在【抽象类】里
2、抽象方法没有花括号,因为不做任何事情
3、子类必须【重写】【所有】抽象父类里的【抽象方法】
4、子类重写方法时【不要】写abstract,然后记得要写花括号,里面写东西
【格式】
在【返回值类型】前加一个abstract,然后不要花括号
public abstract void eat();
错误例子:public class Animal{ ————> 类不是抽象类
public abstract void method(){ ————> 抽象方法只能在抽象类里用,不是抽象类不能用抽象方法
}
}
正确例子:
//这是一个【抽象类】【父类】
public abstract class Animal {
public Animal(){
System.out.println("父类抽象类【构造方法】");
}
public void method(){
System.out.println("父类抽象类【普通方法】");
}
public abstract void eat();
}
//这是一个正常【子类】
public class Dog extends Animal {
//这是重写Animal父类的【抽象方法】,如果不写就会错,子类必须重写【所有】父类的【抽象方法】
//除非!!!这个子类【也是】【抽象类】,那这样这个子类也不能创建对象了,还得有个【孙子】继承【子类】,然后创建【孙子对象】
@Override
public void eat(){
System.out.println("狗吃屎 子类【重写】父类【抽象eat方法】");
}
@Override
public void method(){
super.method();
System.out.println("子类【重写】父类【普通方法】");
}
public Dog(){
//super()赠送子类,子类【构造方法】调用父类【构造方法】
}
}
//这是一个测试类
public class test {
public static void main(String[] args){
//Animal animal = new Animal();抽象类不可以创建对象,理解:你创建一个动物怎么创建?是狗?还是猫?还是鱼?
//必须用一个子类继承这个【抽象类】,然后创建【子类】对象
Dog dog = new Dog();// ————> 这个输出父类【构造方法】,也即"父类抽象类【构造方法】"
dog.eat();// ————> 这个输出子类重写父类的【抽象方法】,也即"狗吃屎 子类【重写】父类【抽象eat方法】"
dog.method();// ————> 这个先输出父类【普通方法】,再输出子类重写父类的【普通方法】,也即"父类抽象类【普通方法】","子类【重写】父类【普通方法】"
}
}
---------------------------------------------------------------------------------------------------------------------------------
【接口】
理解:【接口】就是一种“行为”、“动作”(方法)的【规则、标准】,可以理解为是针对于【行为】的一种【抽象】!!!
前面说的【继承、抽象】,那是对一类具有共性事物的统一,比如“老师”、“学生”、“小混混”、“娘炮”都可以 ————> 归属为“人”
那么【接口】是对具有共性行为的统一,比如”狗刨“、”蛙泳“都可以 ————> 归属为“游泳”,再比如”用飞机运输“、”邮政快递“、”高铁专线快送“、”人走路给你送过来“ ————> 归属为“搬运货物”
不需要管它做这个事的主体是人是鬼是什么玩意,比如”游泳“这个行为,在【Animal动物】这个抽象类里,【狗类】【蛙类】可以拥有,而【猫类】没有,而【Person人类】里的【学生类】【教师类】。。。也都可以拥有这个方法
那么为什么不直接用抽象类里的抽象方法不就好了?抽象方法不就是对【行为】的抽象吗?
那你想,现在如果在【Animal动物】这个抽象类里设置了【游泳】这个【抽象方法】,是不是子类都必须得重写,可是【猫】【鸡】【牛】。。。这些人家不需要啊
还有【Person人类】的【子类】怎么办,人家又没继承【Animal动物】,就不让人游泳了吗?
【所以总结:【接口】就是对一种【方法】的【规则、标准】】
【再简单就是,【接口】就理解为【动作”类“】(但注意,只是为了方便理解,类的关键字是class,接口关键字是interface)】
【格式】
【接口】:public interface 接口名 {
......
}
【实现类】:public class 类名 implements 接口名1,接口名2,接口名3...{
......
}
注意:1、接口实现类必须重写接口的所有抽象方法,除非这个实现类也是一个抽象类(跟抽象子类继承父类的规则一样)
2、接口实现类可以同时实现【多个】接口,例子:
public class 类名 implements 接口名1,接口名2,接口名3...{
......
}
3、接口实现类可以在【继承】某一父类的【同时】实现多个接口,例子:
public class 类名 extends 父类 implements 接口名 {
......
}
这些有什么用呢?具体代码展示:
//这是Swim【接口】
public interface Swim {
public abstract void swim();
}
//一个Animal【父类】
public abstract class Animal {
......
public abstract void eat(); //一个抽象eat方法,动物们都共有的行为,应该不会有哪个动物不会
}
//另一个Person【父类】
public abstract class Person {
......
public abstract void play();
}
//一个Animal的【子类】(【不】具有【Swim】这个行为)
public class Rabbit extends Animal{
//兔子不会游泳,所以只继承Animal,但不用实现Swim接口,自然也不用重写Swim接口的抽象方法
@Override
public void eat() {
System.out.println("兔子吃萝卜");
}
}
//一个Animal的【子类】(也具有【Swim】这个行为)
public class Frog extends Animal implements Swim {
//青蛙继承Animal的抽象类eat的同时,还要重写实现Swim接口的功能
@Override
public void swim() {
System.out.println("青蛙蛙泳唧唧呱呱");
}
@Override
public void eat(){
System.out.println("青蛙啥都吃");
}
}
//一个Animal的【子类】(也具有【Swim】这个行为)
public class Dog extends Animal implements Swim{
//狗继承Animal的抽象类eat的同时,还要重写实现Swim接口的功能
@Override
public void swim(){
System.out.println("狗子狗刨喔喔汪汪");
}
@Override
public void eat(){
System.out.println("狗吃屎");
}
}
//一个Person的【子类】(也具有【Swim】这个行为)
public class Man extends Person implements Swim{
@Override
public void play(){
System.out.println("男人健身");
}
//这里man类继承的是Person类,跟Animal类一点关系没有,但是也可以和Animal的子类共享Swim这个接口
@Override
public void swim(){
System.out.println("男人游泳会自由泳、蝶泳、蛙泳、潜水");
}
}
//一个【测试类】
public class test_接口关系 {
public static void main(String[] args){
Frog frog = new Frog();
frog.setName("青蛙");
Dog dog = new Dog();
dog.setName("狗");
Rabbit rabbit = new Rabbit();
rabbit.setName("兔子");
Man man = new Man();
man.setName("Mike");
man.setAge(18);
frog.swim(); ————> 输出"青蛙蛙泳唧唧呱呱"
dog.swim(); ————> 输出"狗子狗刨喔喔汪汪"
// rabbit.swim();因为rabbit不会swim,所以我没有让它来实现这个接口,因此它自然也就没有这个功能方法
man.swim(); ————> 输出"男人游泳会自由泳、蝶泳、蛙泳、潜水"
}
}
最后注意各个类直接的继承关系:
1、正常父类(包括抽象类)与类之间的继承,可以多层继承(太公—>爷爷—>爸爸—>儿子—>孙子),但是【不可以】多个父类继承(一个儿子多个爹)
2、接口与类之间继承,可以【多继承】(一个人可以拥有人的智商、猎豹的速度、狗的傻气),也可以【多实现】(人也可以游泳、狗也可以游泳、青蛙也可以游泳)、(当一个实现子类实现了多个接口,而这多个接口有多个重名的抽象方法时,实现子类里只需要重写一遍同名抽象方法)
比如:
接口一{抽象方法1,抽象方法2}
接口二{抽象方法1,抽象方法2,抽象方法3,抽象方法4}
实现类 实现 接口一,接口二{抽象方法1,抽象方法2,抽象方法3,抽象方法4} ————> 这里接口一和接口二都有名一样的抽象方法“抽象方法1,抽象方法2”,不需要多次重写,重写一次就够了
3、接口与接口之间继承,可以【单继承】也可以【多继承】
如果一个接口是最下面的子接口,也就是继承了上面的接口,那么当一个实现子类实现的接口是这个最下面的子接口时,要把被继承的所有接口的抽象方法都继承)
比如:
接口一{抽象方法1}
接口二{抽象方法2}
接口三 继承 接口一,接口二{抽象方法3} ————> 这里接口三继承了接口一和接口二,并拥有自己特有的抽象方法“3”
实现类 实现 接口三{抽象方法1,抽象方法2,抽象方法3} ————> 这里接口一到接口三的所有“抽象方法”都重写一遍
另外,接口可以有抽象方法,也可以没有,用上default关键字就是默认方法,可以重写可以不重写
但是如果各个接口有同名的普通方法,那么就算不是抽象方法也得重写,不然在测试类里调用这个方法,就蒙了。
还有static的修饰的方法,实现类不能重写,就算强制写了static同名方法,也只是一个单独的同名方法,两个同名方法之间没有关系
///
///
【多态】
多态就是:一个类型可以是父类也可以子类,
比如:
Father father = new Son(); father = (Son)father;
(注意只有父亲有这个特权,儿子还想当爹?做梦!)
多态的前提:1、多态前提,有父类子类继承、接口实现这些关系
2、有方法的重写
多态的好处:使用【父态】做方法的参数,接收所有子类信息
例子:
//先建立一个【父类Person】
package 多态关系;
public class Person {
private String name;
private int age;
public Person(){
}
public Person(String name,int age){
this.name = name;
this.age = age;
}
public void setName(String name){
this.name = name;
}
public void setAge(int age){
this.age = age;
}
public String getName(){
return this.name;
}
public int getAge(){
return this.age;
}
//这个是自定义的输出一下这个人信息的方法,让子类继承去改一下
public void method(){
System.out.println("这个人的姓名是:" + getName() + ",年龄是:" + getAge());
}
}
//建立一个【学生类】继承【父类Person】
public class Student extends Person{
@Override
public void method(){
System.out.println("这学生的姓名是:" + getName() + ",年龄是:" + getAge());
}
}
//建立一个【教师类】继承【父类Person】
public class Teacher extends Person{
@Override
public void method(){
System.out.println("这老师的姓名是:" + getName() + ",年龄是:" + getAge());
}
}
//建立一个【管理员类】继承【父类Person】
public class Adminster extends Person{
@Override
public void method(){
System.out.println("这管理员的姓名是:" + getName() + ",年龄是:" + getAge());
}
}
//这是一个测试类
public class Test_多态关系{
public static void main(String[] args){
Teacher teacher = new Teacher();
teacher.setName("沈瑞琳");
teacher.setAge(25);
Student student = new Student();
student.setName("岑梓铭");
student.setAge(22);
Adminster adminster = new Adminster();
adminster.setName("管理员老爹");
adminster.setAge(100);
register(teacher);//类似执行这种操作Person person = new Teacher();
// teacher.setName("沈瑞琳");
// teacher.setAge(25);
register(student);//类似执行这种操作Person person = new Student();
// ......
register(adminster);//类似执行这种操作Person person = new Adiminster();
// ......
}
public static void register(Person person){//多态的好处!!!使用【父态】做参数,接收所有子类信息
person.method();
}
// 如果不用多态这么方便的方法,那么register就要分别传入每一个用户“老师、学生、管理员”
// public static void register(Teacher teacher){
// teacher.method();
// }
// public static void register(Student student){
// student.method();
// }
// public static void register(Administer administer){
// administer.method();
// }
// 又或者有人会想我直接一个方法,参数列表用其他形参不行吗?不行,报错,因为这参数是对象
}
不过【多态】有优势也有弊端
注意:1、多态访问变量,编译找左边(左边类型有这个变量就不报错),运行找左边(运行结果是左边的类型的变量)
2、多态访问方法,编译找左边(左边类型有这个方法就不报错),运行找右边(运行结果是右边的类型的方法)
弊端:父类类型创建一个子类类型的对象,没办法使用子类的特有的方法(在编译检查时会出错)例子:
//同一页里的类只能有一个public
//这是一个【父类】
class Father{
String name = "father";
public void method(){
System.out.println("父类方法");
}
}
//这是一个继承【子类】
class Son extends Father{
String name = "son";
public void method(){
System.out.println("儿子方法");
}
public void method2(){
System.out.println("儿子想尿尿");
}
}
//这也是一个继承【子类】
class Daughter extends Father{
String name = "daughter";
public void method(){
System.out.println("女儿方法");
}
public void method2(){
System.out.println("女儿想尿尿");
}
}
//这是一个测试类
public class 测试多态优势和弊端 {
public static void main(String[] args){
Father a = new Son();
//多态访问变量,编译找左边,运行找左边
System.out.println(a.name);//————>输出Father的name,这里运行找父类的变量
//多态访问方法,编译找左边,运行找右边
a.method();//————>输出Son的方法,这里运行找子类的方法
// a.method2();报错,这里编译时先找左边,Father父类没有method2()这个方法,所以找不到报错
//这样就出现弊端,没办法使用【子类】的【特有方法】,怎么办?想尿尿却尿不了
//强制类型转换就行
// Son son = (Son) a;
// son.method2();//现在就不会报错了,不过这样直接转不稳妥
//注意,转换的类型是之前多态里提到的类型,不能转【其他子类】
//instanceof是【比较对象】的关键字,是同类型就true,不是就false
if(a instanceof Son){
Son son = (Son) a;
}else if(a instanceof Daughter){
Daughter daughter = (Daughter) a;
}
//先行判断这个对象是不是这个类型,是就转换,这才是稳妥方式
}
}
=========================================================================
===============================【内部类】==================================
1、【成员内部类】
2、【静态内部类】
3、【局部内部类】
【内部类】就是写在一个类内部的类比如例子:
//这是一个外部类
public class Car{
String carBrand;
String carColor;
public void show(String carBrand,String carColor){
System.out.println("车子品牌:"+carBrand+",车子颜色:"+carColor);
//System.out.println("测试车子类能否调用发动机类——>"+"发动机品牌:"+engineBrand+"发动机颜色:"+engineColor);
//外部类无法直接访问内部类成员
Engine engine = new Engine();//只能先创建一个内部类对象,再去访问其内部成员
System.out.println("测试车子类能否调用发动机类——>"+"发动机品牌:"+engine.engineBrand+" 发动机颜色:"+engine.engineColor);
}
//------------------------------------------------------------------------------------------------------------------------
//这就是一个内部类 | |
|public class Engine{ |
| String engineBrand; |
| String engineColor; |
| public void show(String engineBrand,String engineColor){ |
| System.out.println("发动机品牌:"+engineBrand+" 发动机颜色:"+engineColor); |
| } |
|} |
//------------------------------------------------------------------------------------------------------------------------
}
//这是一个测试类
public class test_内部类 {
public static void main(String[] args){
Car car = new Car();
//创建内部类对象
//不过要注意,这个类的修饰符得符合当前文件权限要求,比如private在这就不可以new一个对象
Car.Engine engine = new Car().new Engine();
car.show("宝马","红色");
Object engine = car.getInstance("岑梓铭牌马达","黑色");
}
}
上面例子就是,一个【汽车类】,【内部】还有一个【发动机类】,你不能用继承这些关系,因为他们不是一类东西,他们是【包含】关系,比如【人类】和身体里的【内脏类】,【手机】和【手机零配件】
这更类似【C语言】的【结构体】,然后注意,创建【内部类对象】的格式:“外部类型.内部类型 类型名 = new 外部类型().内部类型()”
“【外部类型.内部类型】”这个就是【数据类型】,所以没有【()中括号】,就好像 “int[] a = new int[10];”里的“int[]”
而“new 对象”就是一个【对象】,
比如:
System.out.println(new Car().carBrand);
这样是可以直接访问carBrand这个成员变量,只是没有【对象变量名】接收而已
【内部类】也就理解为【一个成员】
所以要在外部类建立一个内部类对象,就是“外部类.内部类 对象名 = new 外部类().内部类();”
【总结:1、类型不用() 2、对象要带() 3、记住两个对象都要带new!!!】
不过如果这个内部类你希望跟私有变量一样不轻易被人在外面乱改变的话,可以用private修饰,那么一样给它值就要像什么getName,getAge......这样设
例子:
//这是一个外部类
public class Car{
String carBrand;
String carColor;
public void show(String carBrand,String carColor){
System.out.println("车子品牌:"+carBrand+",车子颜色:"+carColor);
//System.out.println("测试车子类能否调用发动机类——>"+"发动机品牌:"+engineBrand+"发动机颜色:"+engineColor);
//外部类无法直接访问内部类成员
Engine engine = new Engine();//只能先创建一个内部类对象,再去访问其内部成员
System.out.println("测试车子类能否调用发动机类——>"+"发动机品牌:"+engine.engineBrand+" 发动机颜色:"+engine.engineColor);
}
//------------------------------------------------------------------------------------------------------------------------
//这就是一个内部类
| |
|private class Engine{ |
| String engineBrand; |
| String engineColor; |
| public void show(String engineBrand,String engineColor){ |
| System.out.println("发动机品牌:"+engineBrand+" 发动机颜色:"+engineColor); |
| } |
|} |
//------------------------------------------------------------------------------------------------------------------------
//getInstance(不一定叫这名,规范自定义方法名而已)就是让外部类获得内部私有类的方法,类似什么getName,getAge......
//如果用了getInstance方法获得【内部类】的话,就不要再去测试类里用【创建内部类对象】的方法访问内部成员类,二选一即可
public Engine getInstance(String engineBrand,String engineColor){
System.out.println("发动机品牌:"+engineBrand+" 发动机颜色:"+engineColor);
return new Engine();//注意是“new Engine()”,而【不是!!】“Engine”,要创建一个【实例对象】来返回
}
}
//这是一个测试类
public class test_内部类 {
public static void main(String[] args){
Car car = new Car();
car.show("宝马","红色");
Object engine = car.getInstance("岑梓铭牌马达","黑色");
//2、如果用getInstance方法获得【内部类】的话,就不要用【创建内部类对象】的方法访问内部成员类,二选一即可
//并且这就是用【多态】的想法,用【父类】定义【子类】对象
//至于为什么用object类型,而不是car engine = car.getInstance();呢?
//因为Engine这个【内部类】并没有继承car这个外部类,没有爹,而【Object】是所有类的【父类】
}
}
然后内部类还主要分为:1、【成员内部类】,刚刚上面的就是,没有static修饰就是
2、【静态内部类】,static修饰的就叫【静态内部类】例子:
//这是一个外部类
public class Outer {
int a = 1;
static int b = 20;
//成员内部类-------------------------------------------------------------------------------------------------------------
public class Inner1{ |
| int a = 2; |
| |
| public void show(){ |
| int a = 3; |
| System.out.println(a);// ————> 输出【局部方法】里的 “a = 3” |
| System.out.println(this.a);// ————> 输出【本类Inner1】里的 “a = 2” |
| System.out.println(Outer.this.a);// ————> 输出【外部类Outer】里的 “a = 1” |
| |
| //注意!!这样是错的!!!System.out.println(Outer.Inner1.a); 或者 System.out.println(Outer.a); |
| |
| //因为【类.变量】、【类.方法】、【类.类】,这种形式只适用于【static】修饰的【静态变量】、【静态方法】、【静态类】 |
| //static修饰的变量、方法、类,叫【静态内部成员】,属于【类的内部成员】,不需要创建【对象】就可以直接访问 |
| //而没有static修饰的变量、方法、类叫【非静态内部成员】,属于【对象的内部成员】,必须创建了实例对象才可以访问:【对象.变量】 |
| //所以这里Outer.Inner1.a这种写法是错的,计算机会认为Outer和Inner1是由static修饰的【静态类】,但是发现并没有 |
| //所以正确方式是 |
| // 1、【Outer.this.a】,用【外部类名.this】代表外部类对象,获取外部类对象地址值 |
| // System.out.println(Outer.this.a); |
| // 2、【new Outer().a】,【非静态变量】就老老实实靠创建实例对象再访问 |
| //System.out.println(new Outer().a); |
| //【new Outer().new Inner1().a】 也等于 【this.a】 |
| } |
}-----------------------------------------------------------------------------------------------------------------------|
//静态内部类-------------------------------------------------------------------------------------------
static class Inner2{ |
| int a = 4; |
| public void show1(){ |
| System.out.println("【静态内部类】里的【非静态方法】被调用!"); |
| } |
| |
| public static void show2(){ |
| |
| System.out.println("【静态内部类】里的【静态方法】被调用!"); |
| } |
}----------------------------------------------------------------|
}
//这是一个测试类
public class test_内部类 {
public static void main(String[] args){
//1、创建【成员内部类】对象的正确格式
Outer.Inner1 inner1 = new Outer().new Inner1(); ————> 区别,【成员内部类对象需要两new,也就是外部类和内部类都要一起创建对象,而静态内部类里不需要创建外部类对象,一个new够了】
//2、创建【静态内部类】对象的正确格式
Outer.Inner2 inner2 = new Outer.Inner2();
//普通内部类的方法必须要通过先【创建外部类对象】—>再【创建内部类对象】—>再调用
Outer out = new Outer();
out.new Inner1().show();
//show1是【静态内部类】里的【非静态方法】,【非静态方法】调用的前提必须是先创建对象,再调用
inner2.show1();
//show2是【静态内部类】里的【静态方法】,【静态方法】直接"外部类名.内部类名.静态方法名()"既可以用了
Outer.Inner2.show2();
}
}
然后到【局部内部类】:1、【具名局部内部类】
2、【匿名局部内部类】
例子:
//创建一个类,里面设置【方法】,方法里设置【局部内部类】
public class 局部内部类 {
public class Out{
//注意,这里我犯了个小错误,我本来想在【测试类】的【main方法】里直接尝试【创建局部类】,结果发现show【方法】一直报错
//原来方法里不可以有别的方法体,可以调用,但不可以创建,“套娃方法”达咩,只能在类里创建方法
public void show(){
//public class 一个局部类{
// 这样是错的,【局部内部类】里【不可】以用【public】修饰
//}
class 一个局部类{
String a = "dkhg";
int b = 10;
public void method1(){
System.out.println("局部类里的普通方法");
}
public static void method2(){
System.out.println("局部类里的静态方法");
}
}
//要想在局部类外访问局部类的内容,必须得在【外部类】创建一个【局部类对象】
一个局部类 jubulei = new 一个局部类();
System.out.println(jubulei.a); ————> 输出“dkhg”
System.out.println(jubulei.b); ————> 输出“10”
jubulei.method1(); ————> 输出“局部类里的普通方法”
一个局部类.method2(); ————> 输出“局部类里的静态方法”
}
}
}
//一个测试类
public class test_局部内部类 {
public static void main(String[] args){
局部内部类.Out out = new 局部内部类().new Out();
out.show();
}
}