面向对象(OO)
三大特征
封装
- 类:封装对象属性和行为
- 方法:封装一定的业务逻辑功能
- 访问控制修饰符:封装具体的访问权限
继承
作用:代码复用
超类:所有派生类共有的属性和行为
接口:部分派生类共有的属性和行为
派生类:派生类特有的属性和行为
单一继承、多接口实现,具有传递性
- 代码复用,通过extends来实现继承方法
派生类可以访问:派生类的+超类的
超类不能访问派生类的
规定:构造派生类之前必须先构造超类
派生类构造中若不调用超类构造,则默认super()调超类无参构造
super:指当当前对象的超类对象
super.成员变量名—————————访问超类的成员变量
super.方法名()——————————–调用超类的方法
super()——————————————调用超类的构造方法
补充
- super 和 this 不能同时调用方法
- 类属于引用数据类型
- 类里面不赋值,默认值即为数组的默认值(整型 0,引用 null,布尔false,浮点型0.0)
- new n个 对象往往不会在for里面
多态
意义:
同一个对象被造型为不同的类型时,有不同的功能
—对象多态:我、你、水——————-所有对象都是多态的(明天再详细讨论)
同一类型的引用指向不同的对象时,有不同的实现
—行为多态:cut(),move(),getImage()……———所有抽象方法都是多态的
向上造型:
- 超类型的引用指向派生类的对象
- 能点出来什么,看引用的类型
- 能造型成为的类型有:超类+所实现的接口
强转类型转换,成功的条件只有如下两种:
- 引用所指向的对象,就是该类型
- 引用所指向的对象,实现了该接口或继承了该类
强转时若不满足如上条件,则发生ClassCastException类型转换异常
建议:在强转之前先通过instanceof来判断引用指向的对象是否是该类型
类和对象
现实生活中有很多对象组成,基于对象抽出了类
对象:软件中真是存在的单个个体,具有不同的属性
类:一类个体
一般有几个对象,我们专门为此创建几个类
对象为具体,类为模子,一个类可以创建多个对象
类中包含
- 对象的属性/特征——成员变量
- 对象的行为/动作/功能——方法(同一个类不能相同的方法签名)
成员变量会默认初始值
this用法
this:指代当前对象,哪个对象调用方法它指的就是哪个对象
只能用在方法中,方法中访问成员变量之前默认有个this.
this的用法:
this.成员变量名————–访问成员变量
当成员变量和局部变量同名时,若想访问成员变量则this不能省略,其它一般省略
this.方法名()——————调用方法(一般不用,了解)
this()—————————-调用构造方法(一般不用,了解)
class Student { //成员变量 String name; int age; String address; //构造方法 Student(String name,int age,String address){ this.name = name; //zs.name="zhangsan" this.age = age; //zs.age=25 this.address = address; //zs.address="LF" } //实例方法 void study(){ System.out.println(name+"在学习..."); } void sayHi(){ System.out.println("大家好,我叫"+name+",今年"+age+"岁了,家住"+address); } } //构造方法和this的演示 public class ConsDemo { public static void main(String[] args) { //Student zs = new Student(); //编译错误,Student类没有无参构造 Student zs = new Student("zhangsan",25,"LF"); Student ls = new Student("lisi",26,"JMS"); zs.sayHi(); ls.sayHi(); } }
方法和对象
构造方法
构造方法:构造函数、构造器、构建器—————-复用给成员变量初始化代码
作用:给成员变量赋初始值
与类同名,没有返回值类型(连void都没有)
在创建(new)对象时被自动调用
若自己不写构造方法,则编译器默认提供一个无参构造方法,
若自己写了构造方法,则不再默认提供
构造方法可以重载
补充
行参:声明方法变量时
实参:调用方法时
方法签名:方法名+参数列表
方法的重载(overload)
- 发生在同一类中,方法名相同,参数列表不同
- 编译器会根据参数列表的不同自动选择方法
方法的重写(override)
父子类中,方法名相同,参数列表相同(方法签名相同)
在运行时只走子类(new 的 对象)
重写需遵循”两同两小一大”原则:
两同:
- 方法名相同
- 参数列表相同
两小:
- 派生类方法的返回值类型小于或等于超类方法的
- void和基本类型时,必须相等
- 引用类型时,可以小于或等于
- 派生类方法抛出的异常小于或等于超类方法的————API时再说
- 派生类方法的返回值类型小于或等于超类方法的
一大:
- 派生类方法的访问权限大于或等于超类方法的
补充: 静态方法不能被重写
方法的向上造型
- 左边为超类容器,右边为派生类的参数
- 只能调用超类的数据、方法
官方
- 超类型的引用指向派生类的对象
- 能点出来什么,看引用的类型
class 餐馆{
void 做餐(){ 做中餐 }
}
//1)我还是想做中餐-------------不需要重写
class Aoo extends 餐馆{
}
//2)我想改做西餐---------------需要重写
class Aoo extends 餐馆{
void 做餐(){ 做西餐 }
}
//3)我想在中餐之上加入西餐-------需要重写(先super中餐,再加入西餐)
class Aoo extends 餐馆{
void 做餐(){
super.做餐(); + 做西餐
}
}
抽象方法
abstract修饰,只有方法的定义,没有具体的实现(连{}都没有)
抽象类
abstract修饰,包含抽象方法的类必须是抽象类,不能被实例化
需要被继承,派生类:重写所有抽象方法
封装共有的属性和行为—-代码复用 为所有派生类提供统一的类型—–向上造型
为所有派生类提供统一的入口(能点出来),强制必须重写
接口
一种引用数据类型,一种标准
由interf来定义(非修饰)
只包含常量和抽象方法
不能被new对象,不能实例化
接口是要被实现(implements)的,实现/派生类:必须重写所有抽象方法,必须加关键字public
一个类可以实现多个接口,用逗号分隔。若又继承又实现,先继承再实现
接口可以继承接口
补充
- 类和类 继承
- 接口和接口 继承
- 类和接口 实现
内外部类
成员内部类:应用率低
类中套类,外面的称为外部类,里面的称为内部类
内部类通常只服务于外部类,对外不具备可见性
内部类对象通常在外部类中创建
内部类中可以直接访问外部类的成员(包括私有的)
内部类中有个隐式的引用指向了创建它的外部类对象—外部类名.this——API时会用
public class InnerClassDemo { public static void main(String[] args) { Mama m = new Mama(); //Baby b = new Baby(); //编译错误,内部类对外不具备可见性 } } class Mama{ //外部类 private String name; void create(){ Baby b = new Baby(); //正确 } class Baby{ //内部类 void showName(){ System.out.println(name); System.out.println(Mama.this.name); //Mama.this指代当前对象的外部类对象 //System.out.println(this.name); //编译错误,this指代当前Baby对象 } } }
匿名内部类:重点—————–大大简化代码
- 若想创建一个类(派生类)的对象,并且对象只被创建一次,可以做成匿名内部类
- 匿名内部类中不能修饰外面局部变量的值,因为在此处该变量会默认为final的
public class AnonInnerClassDemo { public static void main(String[] args) { //new Aoo(); //创建Aoo对象 //new Aoo(){}; //创建Aoo的派生类的对象 //1)创建了Aoo的一个派生类,但是没有名字 //2)为该派生类创建了一个对象,名为o1 //3)大括号中的为派生类的类体 Aoo o1 = new Aoo(){ //向上造型 }; //1)创建了Aoo的一个派生类,但是没有名字 //2)为该派生类创建了一个对象,名为o2 //3)大括号中的为派生类的类体 Aoo o2 = new Aoo(){ }; int num = 5; num = 55; //1)创建了Boo的一个派生类,但是没有名字 //2)为该派生类创建了一个对象,名为o3 //3)大括号中的为派生类的类体 Boo o3 = new Boo(){ void show(){ //重写Boo类的抽象方法 System.out.println("showshow"); //num = 66; //编译错误,在此处会默认num为final的---API时会用 } }; o3.show(); } } abstract class Boo{ abstract void show(); } abstract class Aoo{ }
关键字
修饰关键词
package
- 避免类冲突
- 同包中类不可以同名,不同包的类可以
- 类的全称包名.类名
- 建议所有包名小写
impor
- 同包中导入类,可以用
- 不同包导入类,需要导入包
- 或者引用类的全称
- 同包中导入类,可以用
访问控制符(导入包 不起作用)
public 公开的
protected 本类 派生类 同包类
默认(不写) 本类 同包类 ——不建议
private 本类
finall:最终的、不可更改的——单独利用率低
- 修饰变量:变量不能被改变,为常量
- 修饰方法:方法不能被重写
- 修饰类: 类不能被继承
static :静态变量
- 属于类
成员变量分为两种
- 实例变量:没有static修饰,属于对象的,存储在堆中,有几个对象就有几个实例变量
需要通过引用打点来访问
静态变量:
由static修饰,属于类,存储在方法区中,只有一份
通过类名打点来访问
何时用:所有对象需要共享的数据(图片,音频,视频)
静态方法:
- 前两条同上
- static将方法体隐式的this删去,不能直接访问实例成员(即静态方法只能访问静态成员)
- 何时用:方法操作和对象无关(不需要访问对象
静态块
- 由static修饰
- 属于类,在类加载的时候自动执行,一个类只加载一次,所以静态块也加载一次
- 何时用:初始化/加载静态资源(图片、音频、视频)
static{
语句块
}
static final(常量)
- 必须声明同时初始化
- 由类名来访问
- 存储在常量池中,常量池存储在堆中
- 建议:常量名所有字母大写,多个单词用_分隔
- 编译器在编译时会将常量直接替换为具体的值,效率高
- 何时用:数据永远不变且经常使用,例如状态
String类(举例)
- java.lang.String 用final修饰,不能被继承(java.lang包可以直接引用)
- 采用Unicode编码,一个字符2字节
- 底层封装了字符的数组
- 字符串一旦创建,对象的内容永远无法改变,但字符串的引用可以重新赋值——不变对象
常量池
java对字符串的优化措施:常量池(堆中)
推荐使用字面量/直接量来创建字符串,并且会缓存所有以字面量形式创建的字符串对象到常量池中,当使用相同字符量在创建字符串会重用对象以减少内存开销
用来存放字符串的地址 ,指向非常量池中的堆中
只能通过字面量相连才会复用,不用使用引用的对象名
String s = new String("hello");
// 以上创建了几个对象
/** * 答 2个
* 第一个: 字面量“hello”
* 创建一个字面量对象表示“hello”,并将其存入常量池
* 第二个:
* new String() 时会再创建一个字符串对象, */
方法
对象名.length(): 获取字符串长度,return int
对象名.trim(): 去除当前字符串两边的空白字符,return一个新对象
对象名.toUpperCase():将当前符号中的英文转换为全大写
对象名.toLowerCase():将当前符号中的英文转换为全小写
全部return String
对象名.startsWith(”sth”):判断字符串是否以”sth“ 开头,return boolea类型
对象名.endsWith(”sth”):判断字符串是否以” sth “ 结尾,return boolea类型
区分大小写
对象名.charAt(int index) : 返回下标处的字符,return char
对象名.indexOf(String str ) :检索str在String开始的下标,return int
对象名.indexOf(String str, int fromIndex ) :检索str在String从下标”fromIndex“以后的下标,return int。其中return的值为差值
对象名.lastIndexOf(String str): 检索str在String最后一次的下标,return int
若找不到目标字符串,返回-1
对象名.substring(int a,int b) :截取下标 a到 b 之间的字符串 return String
对象名.substring(int a) :截取从下标a一直到末尾, return String
String.valueOf(a):将基本数据类型a所代表的值转换为String,return String
StringBuilder类
由于String是不变对象,每次修改内容要创建新对象,因此String不适合做频繁修改操作,为了解决这个问题,java提供了StringBuilder类。
StringBuilder是专门用于修改字符串的一个API,内部维护一个可变的char数组,修改都是在这个数组上进行的,修改速度、性能优秀,并且提供了修改字符串的常见的方法:增、删、改、插
//String不适合频繁修改内容 String s = "a"; for(int i=0;i<10000000;i++){ s = s+i; } System.out.println("执行完毕"); //用StringBuilder可以提高修改字符串的性能 StringBuilder builder = new StringBuilder("a"); for(int i=0;i<10000000;i++){ builder.append(i); } System.out.println("执行完毕");
StringBuilder常用方法:
- append():追加内容
- replace():替换部分内容
- delete():删除部分内容
- insert():插入内容
String str = "好好学习java"; //复制str中的内容到builder中-----好好学习java StringBuilder builder = new StringBuilder(str); //append():追加内容 builder.append(",为了找个好工作!"); System.out.println(builder); //好好学习java,为了找个好工作! //replace():替换部分内容 builder.replace(9,16,"就是为了改变世界"); //替换下标9到15的 System.out.println(builder); //好好学习java,就是为了改变世界! //delete():删除部分内容 builder.delete(0,8); //删除下标0到7的 System.out.println(builder); //,就是为了改变世界! //insert():插入操作 builder.insert(0,"活着"); //从下标0的位置插入 System.out.println(builder); //活着,就是为了改变世界!