认识类和对象
0.常量用final修饰,并且要全部大写,单词间由下划线连接;
1.string类型的默认值是null;
2.int类型的默认值是0;
3.char类型的默认值是‘\u0000’;
4.boolean类型的默认值为false;
方法与重载
5.传参数的时候,基本数据类型传的是值,引用数据类型传的是指针(地址)
6.基本数据类型:加" . "不会出方法的数据类型,例如:char int double 等;
7.引用数据类型:“ . ”的出方法的,例如:数组.length,自定义的class ,接口等;
8.for()遍历数组时,可以写为:for(数据类型 标识符:数组名) eg:for(Student stu : stus);
9.this关键字:
用法1: 带参构造方法中,this指代当前的类:
this.name=name;
this.age=age;
从而与传过来的参数区分开;
用法2:this 关键字可调用类里面的方法
this.showInfo();
用法3:this关键字还可调用构造方法
public student(int age ,string name){xxxxxx}
现在要新增一个构造方法,加入int score 进去,可以调用之前的构造方法以减少代码量
public student(int age,string name ,int score){
this(age,score);
this.score=score;
}
注:用法3的写法中,this()的调用一定要在此构造方法第一句,否则报错。
10.构造方法没有返回类型,即:写构造方法时,不加void int string之类的返回类型;
11.当自定义带参的构造方法之后,原构造方法(系统自带的)失效,如要使用,应自行重新声明
12.构造方法名与类名相同
13.方法重载:当你需要以一个函数名完成不同的功能(比如cal()函数实现两个浮点数,三个浮点数,四个浮点数相加)的情况时,可以使用方法重载,将不同功能的函数使用同一个函数名,方便后续调用;
14.方法重载写法:public或private 以及返回值类型不影响。传参不一样(类型、数量)就可以用相同函数名。
15.成员变量(全局变量)自带初始值;
局部变量不会自动赋初始值。
16.成员变量和局部变量可以同名,局部变量优先级更高。
封装与继承
封装
什么是封装
把类中所有的属性藏起来,把尽可能多的东西藏起来,并提供便捷的接口,避免外部直接访问类信息
如何封装
1.加private
private int health = 100 ;外部就无法访问了,仅类中可访问;
2.添加setter/getter方法
public void setHealth(int health){
xxxxx //判断是否超过限制
this.health = health;
}
public int getHealth(){
return this.health;
}
注:以set/get + 属性名为方法名是规范;
如此,用户必须通过setter和getter方法调用类属性,无法直接访问,避免了类属性的非法更改。
17.setter/getter快捷键:alt+shift+s
包
用途:存放两个同名的类而不冲突,类似文件夹,有助于实施访问权限的控制;
包名由小写字母组成,由小数点分开对应路径,包名前通常加入唯一前缀,通常使用组织倒置的网络域名,但包名后续部分依据不同机构内部规范而确定
包导入语句:import
为了使用不在同一个包的类,需要Java程序使用import关键字导入这个类
import 包名.类名;
eg: import java.util.*; //导入java.util包里的所有类;
import cn.School;//导入指定的包内指定类
18.默认使用同包内的类
19.如果想同时使用不同包的同名类,需要将类的完整限定名写全;
eg:cn.com.oop.Dog dog = new cn.com.oop.Dog();
cn.com.oop.pet.Dog dog2 = new cn.com.oop.pet.Dog();
20.import只会导入当前目录的所有类,不会导入当前目录子目录下的类
eg: import cn.com.oop.*; 不会导入cn.com.oop.pet包下的类;
21.package 放在第一句,然后是import,然后是声明类
访问权限控制
一、类的修饰:public or 什么都不写
eg:public class Test or class Test;
相同包可以访问上述两种,但是不同包只能访问public修饰的类
public:公有访问级别
默认修饰符:包级私有访问级别
二、属性和方法的修饰
private:本类访问only
默认修饰符:同包内访问
protected :不能被其他包访问,可以被同包,子类访问
public : 都可以
static标识符
一、static成员变量 :
实例(对象)变量变为静态变量
static int name ;
可通过类名调用,或实例调用(zzy.name)(不建议)
Person.name;
注:在内存中只有一个拷贝,改变一个就改变了全部的值
二、static方法
public static void m(){}
可直接通过类名调用,也可通过实例调用(不推荐);
静态方法不能使用this和super,不能访问所属类的实例变量,实例方法,只能访问静态变量和静态方法
静态方法必须被实现
main方法是最常用的静态方法,因为它不依赖于任何类,进入类时直接被加载,成为入口
三、static代码块
静态代码块
jvm加载类时,加载静态代码块
按顺序加载多个静态块
每个静态代码块只会被执行一次
eg:
class Test {
static num=100;
static {
num+=100;
print num;
}
}
在main中:Test t1=new Test();
Test t2=new Test();
输出:200 200 (因为每个静态代码块只会执行一次)
继承
避免写重复的代码
将重复代码抽取到父类中
访问修饰符 class Dog extends Pet(){}
注:java中只支持单根继承,一个类只能有一个父类
继承中的修饰符使用
private 子类不能用
默认修饰符 同包内子类可以,不同包子类不行;
protected 子类可用
public 子类可用
即:子类不能继承父类的private成员和不在同一包内的默认修饰符,及构造方法;
方法重写与多态
方法重写
问题来源:子类调用父类的函数(例如:print),不能显示自己所独有的信息。
解决方法:子类重写父类方法(overriding)
构造方法不能重写,只能重写普通方法
方法:调用super ,同名方法
/*此处有几节课断层*/
/*预留位置*/
万物始祖:object类
object类是所有类的祖先(直接或间接父亲)
object有一些方法,其中常用的方法有:
tostring:将类的信息转换为字符串返回;
equals:判断两个类是否相等;
hashcode:返回类的哈希值;
还有一个记不到了,后面再说。
重点:equals():其作用和==相等,都是判断类的一致性,返回一个布尔变量
重点2:equals的判断依据:对象在堆中的地址是否一致
复习:栈与堆:栈:存放索引 堆:存放数据 ,由栈指向堆
重写equals
在默认判断中,equals采用地址作为判断依据,并不具备普适性;
所以很多时候我们需要重写equals
MyEclipse快捷重写方法:鼠标右键找到override method ;
重写流程:
1.判断是否相等(==判断地址是否一致),如真,肯定相等;
2.判断类型是否为当前希望判断的类型(如:两个Student类):
方法:xx(传进来的对象名) instanceof xxx(类名),如真,代表是该类的一个实例;
3.强制类型转换:此时已经确定该对象类型,需要强制转换成当前类型(原来是obj类)从而调出当前类型私有的一些属性(obj作为父类无法调出);
4.判断需要判断的属性是否相等(name,age,money等);
tips:字符串判断相等不用“==”,原因就是上文所说,采取地址判断;
一般判断字符串相等采用equals(),原因:equals已经在string类中被默认重写了,采取全文对比方式;
重写toString
默认的toString返回的是类名+地址,很多时候需要重写为显示需要显示的内容;
重写流程:直接改return 即可;
tips:直接输出对象,如 print(zzy)和 print( zzy.toString )是一样的,可以看成是调用了后者
同样的,对于string来说,已经在默认中被重写了,所以直接输出和tostring输出的都是字符串内容。
多态
设想:你要设计一个会计类,给不同职业的人发钱。怎么写?
1. 在会计类里面用很多if else 写每个职业的薪资;
2.在每个职业类里面写好“发钱”这一方法,会计类直接调用就好;
显然是后者。
那么问题来了,我是不是还得写很多if 来调用不同的方法呢?
No!!!!
Java虚拟机早就想到了这一点,所以对于同一父类的不同子类,只要将父类指向子类(用一个父类的指针指向子类),传参数时传父类过去,Java虚拟机就会自动调用对应的真实子类的方法;
这样的话,我们就可以只在会计类里面写一个“发钱给‘员工类’”就行了,虚拟机会自动调用相应的方法;
抽象方法与抽象类:
在上面的会计例子中,我们在员工类必须写一个空的方法:发钱,因为要给子类重写,不然无法使用多态。
其实,这种没有实现内容的方法,可以被声明为“抽象方法”,因为它只是给子类提供了一个空间。
比如上文的员工类,这个类无法被实例化:一个员工具体是什么呢?你可以实例化经理,服务员,领班,厨师。但公司里面没有一个职位叫“员工”,这种类就叫抽象类;
关于抽象类,还有几个结论/要求:
1.抽象方法没有方法体:显然,你无法给一个“员工”发工资,你只能给具体的职位发工资;
2.抽象方法存在的类一定是抽象类:如果都存在无法被实现的方法了,这个类就一定不是具体的了,只会是抽象的。;
3.抽象类不一定有抽象方法:有时我们只是认定某个类是抽象的,但是不一定马上有对应的抽象方法,可以给后面写的抽象方法留位置;
4.抽象类不能被实例化(new 类名()):显然;
5.抽象方法必须在子类中被实现,除非子类是抽象类:如果不被实现,这个抽象类就没有根基,抽象就没有抽象的源头?(此处存疑,后面学虚拟机之后应该会有更好的答案)
6.如何声明一个抽象方法/类?
在限制符后面加abstract;
向上/向下转型(类型转换)
在前面的例子中,利用将子类由父类引用,从而利用动态绑定机制实现了多态自动匹配相应的方法,这个过程,实际上就是“向上转型”;
向上转型:子类由父类指引,从而实现多态;
向下转型:父类转为子类(常见于向上转型后,要利用子类的私有方法时,要转回来);
为什么会有这两个转型?
向上转型的问题:转型后无法访问子类原有的私有方法,比如,员工类显然无法实现打扫清洁这一属于保洁人员的方法;
此时,就需要用到向下转型,将子类转移回来,从而调用子类的方法;
语法:向上转型:父类+对象名=new 子类();
向下转型:子类+对象名=(子类)+待转对象名;
向下转型又叫做强制类型转换:经理类一定是员工类(向上类型转换),但员工类不一定是经理类(向下类型转换)
所以,问题就出来了:从父类转移到子类的时候,有可能转错了,比如本来是经理,但是转成了服务员(你自己写的时候可能也被搞晕了,更不用说其他代码维护人员了),就会造成一个经典报错:
class cast exception ,即类型转换错误;
为了避免上述错误,一个好习惯是,每次强制类型转换的时候,用“instance of”关键字先判断一下是不是你要转的类型;
即:if a instance of A
//转换;
else if a instance of B
//转换;
直到将所有可能的子类写完,以保证转换的一定正确性。
毕竟,你不知道你的上一任给你留下了什么代码
多态的第二种常用使用方式:父类作为返回值
在上面的写法中,我们使用多态的方法是将父类代替子类传参,在参数调用时调用父类,利用Java的动态绑定机制,智能地使用一个调用写法就实现了多次的判断。
实际上,常用的多态使用方法,还可以将父类作为返回值:
还是那个员工的例子,我作为一个老板类,要找手下的员工帮我去买个东西,我找不同的员工,会买不同的东西,比如找经理买账本,找厨师买食材等等。
那么我正常的思路是:在测试类中询问要买神马,再进行Switch判断,最后将值传回老板类,由老板类进行调用不同的员工去买东西。
但是这样写,会导致测试类里面内容冗杂,本来该放到老板类处理的判断放在了测试类中。我的测试类实际上只需要最后返回的一个员工类而已;
所以我们将Switch放入老板类。
但是问题又来了:一个函数只能有一个返回类型,我们有那么多个员工类,要每个都写一个函数吗?
未免太麻烦了,也不利于后期维护。
所以我们想到了多态,我们能不能返回一个父类,利用多态的动态绑定机制,使得最后返回的这个父类(实际指向的是一个子类),能执行子类的“买东西”方法呢?
可以。我们将返回值设置为父类,在员工类(父类)中写一个abstract的买东西方法,再在子类中重写这个方法,最后在老板类中用Switch判断后new一个子类,再将一个父类指针指向这个子类。最后返回父类。
然后在测试类中,可以直接使用“父类.购物”,得益于上述的重写和动态绑定机制。
抽象类和接口
抽象类的补充:
1.为什么抽象类不能被实例化?
从语义来说,抽象类实例化毫无意义,提到猫,你会想到一只毛茸茸的球,但是提到宠物,如何将它具体化呢?
从语法来说,抽象方法没有方法体,在jvm加载之后无法执行这个方法;
2.没有抽象的构造方法:因为抽象方法没有方法体,无法执行;
3.没有抽象的静态方法:静态方法不需要实例化就能执行,但是抽象方法无方法体,无法执行;
4.抽象类可以有非抽象的构造方法,即:为子类提供的构造方法提供合适的调用。
接口:
为什么使用接口
你要写一个防盗门类。基于oop的思想,你需要写一个门类和一个锁类,但是Java只允许单根继承,那么咋办?
由防盗门继承门类,并且实现锁类的接口。
什么是接口?
从广义上说,接口是链接硬件、软件之间的物理或软件工具,在前面学习的setter和getter可以被视作一种接口,然而,在java中的接口,其实更应该看成一种特殊的类。
作为解决Java单根继承问题的工具,接口和类有着很多的相似之处;
声明:public interface MyInterface{
//functions
}
接口有以下特点:(JDK8以前)
0.接口支持单根继承。
1.接口中的变量都是静态常量,且必须初始化 。在使用时和类一样,可以通过“接口名.常量名”使用。
2.接口中的所有方法都必须是public abstract 方法,即,接口内的方法都是没有方法体的。
3.接口不能被实例化,但可以被类实现(implement 接口名1,接口名2,接口名3......),相当于引入了多个父类,只不过此时不叫父类,叫“父接口”
4.在引入父接口之后,子类必须实现父接口的所有方法,除非子类是抽象类。
5.引入父接口之后,可以利用类似类继承中多态的写法,“接口+接口名=new 子类构造方法”,此时,接口可以作为一种数据类型来看待,可以调用与子类所共有的方法;
jdk1.8新增接口特性
1.可以有默认方法,有方法体,能被继承
可有多个默认方法;
语法:default +正常函数声明;
作用:降低了子类和接口间的耦合度;
可能遇到的问题
一、多个接口有同名default,系统不知道用哪个
解决方法1:子类中重写这个方法
解决方法2:在重写中用super指定调用哪个方法
eg:void m2(){
MyInterface.super.m2();//指定调用MyInterface 中的m2方法;
}
二、父类和接口重名
此时默认调用父类方法,如有需要,可按上面的方法重写;
2.JDK1.8允许有静态方法,但必须是public static
由于是静态方法,不能被继承/重写,只能在接口中被调用