Java面向对象
面向对象的特性
一、封装
封装就是把数据和操作数据的方法绑定起来,对数据的访问只能通过已定义的接口。可以说,封装就是隐藏一切可隐藏的东西,只向外界提供最简单的编程接口。
- 原则:将属性隐藏起来,若需要访问某个属性,提供公共方法对其访问。
- 封装的操作步骤:
1、使用 private 关键字来修饰成员变量。
2、对需要访问的成员变量,提供对应的一对get 、set方法。 - private的含义:
1、private是一个权限修饰符,代表最小权限。
2、可以修饰成员变量和成员方法。
3、被private修饰后的成员变量和成员方法,只在本类中才能访问。
public class Student {
private String name;
private int age;
// 无参数构造方法
public Student() {}
// 有参数构造方法
public Student(String name,int age) {
this.name = name;
this.age = age;
}
public void setName(String name) {
//name = name;
this.name = name;
}
public String getName() {
return name;
}
}
this代表所在类的当前对象的引用(地址值),即对象自己的引用。
使用 this 修饰方法中的变量,解决成员变量被隐藏的问题
当一个对象被创建时候,构造方法用来初始化该对象,给对象的成员变量赋初始值。
所有的类都有构造方法,因为Java自动提供了一个无参数构造方法,
一旦自己定义了构造方法,默认无参数构造方法就会失效。
即使还需要无参构造方法也要自己定义。
JavaBean(了解)
JavaBean 是 Java语言编写类的一种标准规范。
符合 JavaBean 的类,要求类必须是具体的和公共的,并且具有无参数的构造方法,提供用来操作成员变量的 set 和 get 方法。
二、继承
将一类事物共有的属性和行为提取成一个父类,而每一个子类是一个特殊的父类——有父类的行为和属性,也有自己特有的行为和属性。
这样做扩展了已存在的代码块,进一步提高了代码的复用性。
-
继承的作用:
子类可以直接访问父类中的非私有的属性和行为,但无法继承private 修饰的方法和属性。- 所有类都是Object的子类
- 父类:被继承的类,也可以成为超类或基类
- 子类:继承的类,也可以称为派生类
- 继承需要合乎情理,不能乱继承
-
继承的格式:
class B extends A{ } //B继承A,A是B的父类,B是A的子类
-
继承说明:一个类最多只有一个父类,但一个类可以有多个子类(如Object类)
-
既然Object是所有类的父类,所以Object类中的属性和方法是可以直接用的
继承后的特点
a、成员变量
- 父类子类成员变量不重名———访问没有影响
- 父类子类成员变量重名
在子类中需要访问父类中非私有成员变量时,需要使用 super 关键字,修饰父类成员变量,类似于 this 。
super.父类成员变量名
class Zi extends Fu {
// Zi中的成员变量
int num = 6;
public void show() {
//访问父类中的num
System.out.println("Fu num=" + super.num);
//访问子类中的num
System.out.println("Zi num=" + this.num);
}
}
Fu 类中的成员变量是非私有的,子类中可以直接访问
若Fu 类中的成员变量私有了,子类是不能直接访问的。
通常编码时,我们遵循封装的原则,使用private修饰成员变量,
访问父类的私有成员变量
可以在父类中提供公共的getXxx方法和setXxx方法。
b、成员方法
- 成员方法不重名———访问没有影响
对象调用方法时,会先在子类中查找有没有对应的方法,若子类中存在就会执行子类中的方法,若子类中不存在就会执行父类中相应的方法
- 成员方法重名———重写(Override) 代码复用性体现
方法重写 :子类中出现与父类一模一样的方法时
(返回值类型,方法名和参数列表都相同),会出现覆盖效果,也称为重写或者复写。
重写应用:
子类可以根据需要,定义特定于自己的行为。
既沿袭了父类的功能名称,又根据子类的需要重新实现父类方法,从而进行扩展增强。
比如新的手机增加来电显示头像的功能,代码如下:
class Phone {
public void sendMessage(){
System.out.println("发短信");
}
public void call(){
System.out.println("打电话");
}
public void showNum(){
System.out.println("来电显示号码");
}
}
class NewPhone extends Phone {
//重写父类的来电显示号码功能,并增加自己的显示姓名和图片功能
public void showNum(){
//调用父类已经存在的功能使用super
super.showNum();
//增加自己特有显示姓名和图片功能
System.out.println("显示来电姓名");
System.out.println("显示头像");
}
}
这里重写时,用到super.父类成员方法,表示调用父类的成员方法。
super :代表父类的存储空间标识(可以理解为父亲的引用)。
this :代表当前对象的引用(谁调用就代表谁)。
c、构造方法
- 构造方法的名字是与类名一致的。所以子类是无法继承父类构造方法的。
- 构造方法的作用是初始化成员变量的。所以子类的初始化过程中,必须先执行父类的初始化动作。子类的构造方法中默认有一个 super() ,表示调用父类的构造方法,父类成员变量初始化后,才可以给子类使用。
d、父类空间优先于子类对象产生(子类对象中包含了其对应的父类空间)
在每次创建子类对象时,先初始化父类空间,再创建其子类对象本身。
三、抽象类
概述
- 有时候,我们可能想要构造一个很抽象的父类对象,它可能仅仅代表一个分类或抽象概念,它的实例没有任何意义,因此不希望它能被实例化。
- 通过在class关键字前增加abstract修饰符,就可以将一个类定义成抽象类。抽象类不能被实例化。
- Java语法规定,包含抽象方法的类就是抽象类,但抽象类不一定含抽象方法。
抽象方法
父类中的方法,被它的子类们重写,子类各自的实现都不尽相同。
那么父类的方法声明和方法主体,只有声明还有意义,而方法主体则没有存在的意义了。我们把没有方法主体的方法称为抽象方法。
使用 abstract 关键字修饰方法,该方法就成了抽象方法。
定义格式:
修饰符 abstract 返回值类型 方法名 (参数列表);
代码举例:
public abstract void run();
抽象类
public abstract class Animal {
public abstract void run();
}
抽象方法的使用
继承抽象类的子类必须重写父类所有的抽象方法。
否则,该子类也必须声明为抽象类。
最终,必须有子类实现该父类所有的抽象方法,
否则,从最初的父类到最终的子类都不能创建对象,失去意义。
抽象类不能创建对象,只能创建其非抽象子类的对象。
抽象类中可以有构造方法,是供子类创建对象时,初始化父类成员使用的。
抽象类中不止含有抽象方法。还可以含有成员变量、成员方法等。
四、接口
- 接口的内部主要就是封装了方法,包含抽象方法,默认方法和静态方法(JDK 8),私有方法 (JDK 9)。
- 接口的定义,使用 interface 关键字。它也会被编译成.class文件,但一定要明确它并不是类,而是另外一种引用数据类型。(引用数据类型:数组,类,接口)
- 接口的使用,它不能创建对象,但是可以被实现( implements ,类似于被继承)。一个实现接口的类(可以看做是接口的子类),需要实现接口中所有的抽象方法,创建该类对象,就可以调用方法了,否则它必须是一个抽象类。
public interface 接口名称 {
// 抽象方法
// 默认方法
// 静态方法
// 私有方法
}
抽象方法:使用 abstract 关键字修饰,可以省略,没有方法体。该方法供子类实现使用。
public abstract void method();
默认方法:使用 default 修饰,不可省略,供子类调用或者子类重写。
静态方法:使用 static 修饰,供接口直接调用。
public default void method() { // 执行语句 }
public static void method2() { // 执行语句 }
私有方法:使用 private 修饰,供接口中的默认方法或者静态方法调用。
接口实现
- 必须重写接口中所有抽象方法。
- 继承了接口的默认方法,即可以直接调用,也可以重写。
class 类名 implements 接口名 {
// 重写接口中抽象方法【必须】
// 重写接口中默认方法【可选】
}
接口的多实现
- 一个类只能继承一个父类
- 一个类可以实现多个接口
- 一个接口可以继承多个接口
- 以上可以同时存在
多态应用1:接口作为成员变量:(赋给它该接口的一个子类对象)
public interface FaShuSkill {
public abstract void faShuAttack();
}
public class Role {
FaShuSkill fs;
public void setFaShuSkill(FaShuSkill fs) {
this.fs = fs;
}
//接口作为成员变量时,对它进行赋值的操作,实际上,是赋给它该接口的一个子类对象。
...
多态应用2:接口作为返回类型和方法参数:(其实都是它的子类对象)
public List<Integer> get(List<Integer> list) {
ArrayList<Integer> evenList = new ArrayList<>();
return evenList;
// List 接口作为参数或者返回值类型时,可以将 ArrayList的对象进行传递或返回
}
如果抽象方法有重名的,只需要重写一次。
如果默认方法有重名的,必须重写一次。
存在同名的静态方法并不会冲突,原因是只能通过各自接口名访问静态方法。
接口中,无法定义成员变量,但是可以定义常量,其值不可以改变,默认使用public static final修饰。
接口中,没有构造方法,不能创建对象。
接口中,没有静态代码块。
抽象类与接口的区别:
- 接口中变量都是静态(static)的常量(final)(全局常量);抽象类中可以有实例变量。
- 访问权限
- 多实现与单继承
- 允许存在构造方法
- 抽象类继承:IS-A ;接口实现:HAS-A
继承与实现接口的区别:
使用继承,可以减少代码量,常用方法可以不必定义,而是直接继承父类定义好了的方法,提高编程效率。体现了软件的三特性之一的可复用性。
使用接口,只定义方法,没有具体的方法体,实现该接口的类可以对接口中的方法灵活的根据实际情况定义,很好的是程序具有灵活、复用的特性。
java允许一个接口继承多个父接口,也允许一个类实现多个接口,而这样的多继承多实现有缺点吗?
答案是没有,这是由接口的抽象性决定的。
1)正如前面介绍的,在接口中不能有实例变量,只能有静态的常量,只有抽象方法(即使8以后的默认方法同名,也会要求重写)
2)对于一个类实现多个接口,因为接口只有抽象方法,具体方法只能由实现接口的类实现,在调用的时候始终只会调用实现类的方法(不存在歧义)就算要调用父接口同名的默认方法,也只会调用直接实现接口中的 重写后的默认方法;
参考文章(时代久远,jdk1.8后接口中不止抽象方法,可含有方法体)
五、多态
多态存在的必要条件:
- 必须要有继承或者实现 (Person 由Student,Teacher继承)
- 要有重写 /Person(eat()) Student(eat(重写))
- 父类引用指向子类对象
Person per=new Student();
- 格式:父类类型 变量名 = new 子类对象;
多态的使用:
当使用多态方式调用方法时,首先检查父类中是否有该方法,如果没有,则编译错误;
如果有,执行的是子类重写后方法。(父类执行)
多态的好处:
降低代码之间的耦合度,利于程序扩展和代码通用性
父类类型作为方法形式参数,传递子类对象给方法。(根据需求传递不同的子类对象)
引用类型转换:
- 多态的转型分为向上转型与向下转型两种:
- 向上转型:默认的,当父类引用指向一个子类对象时,便是向上转型。
- 向下转型:强制转换,向上转型的子类对象,将父类引用转为子类引用
向下转型:子类类型 变量名 = (子类类型) 父类变量名;
使用条件:父类变量想要使用子类的特有的方法时。
六、final 关键字
用于修饰不可改变内容
- 类:被修饰的类,不能被继承。
- 方法:被修饰的方法,不能被重写。
- 变量:被修饰的变量,不能被重新赋值。
修饰变量:
局部变量——>基本类型
- 只能赋值一次,不能再更改。
局部变量——>引用类型
- 只能指向一个对象,地址不能再更改
成员变量:只能赋值一次(直接赋值 / 构造器赋值)
七、匿名内部类
将一个类A定义在另一个类B里面,里面的那个类A就称为内部类,B则称为外部类。
匿名内部类是内部类的简化写法。
前提:匿名内部类必须继承一个父类或者实现一个父接口。
public class InnerDemo {
public static void main(String[] args) {
/*1.等号右边:是匿名内部类,定义并创建该接口的子类对象
2.等号左边:是多态赋值,接口类型引用指向子类对象 */
FlyAble f = new FlyAble(){
public void fly() {
System.out.println("我飞了~~~");
}
};
//用法一、调用 fly方法,执行重写后的方法
f.fly();
//用法二、将f传递给showFly方法中
showFly(f);
}
}
异常类
- Exception类是所有异常类的根类
- 处理格式:
try{
//可能发生异常的程序
}catch(Exception e){
//对异常进行处理
}
//补充:可以使用多个catch分别针对不同的异常进行捕获
在myeclipse代码块右击选择source中的format可以整理代码
Alt +/ :智能补充
Ctrl +1:智能提示解决报错的方法
CTRL+shift+O:导入所有需要的包
ALT+shift+S:生成对话框可直接生成private属性下的Get和Set方法;也可以生成有参和无参的构造函数。
IO流
- 定义:
IO流 :Input/Output | 输入/输出
输入————读取
输出————写出去 - 流的分类:
a.字符流 b.字节流
字符流读取文本文档中的内容:
FileReader类
FileReader fr = new FileReader("文件名"); //这个类已经关联到一个文件上
//
fr.read(c)=-1表示已经读完整个文件
//
while(fr.read(c)!=-1){ System.out.println(c); }
//利用循环,在一个小的空间迭代覆盖读出一个大的文件,可节省空间
//报错:找不到指定文件
try {
FileReader fr=new FileReader("hello.txt");
} catch (FileNotFoundException e) {
e.printStackTrace();
}
//需要加上完整路径:E:\\jdk\\hello.txt(\\有一个是转义符)
char[] c = new char[20];
int num = fr.read(c); //是将我们的内容读到一个字符数组中 num就代表读到的长度
System.out.println(fr.read());
// 由于所有内容都读进了数组中,所以fr.read()读到的是-1,文件的末尾标志
String str = new String(c, 0, num); // 定义String用到的截取方法
System.out.println(str);
char[] c= new char [20];
fr.read(c);
while(fr.read(c)!=-1){
System.out.println(c);
}
/*fr.read(c)每次读,下次就会记住读到的位置,如果到了文件末尾则返回值为-1,
不然返回值就是这次读到的长度*/
利用字符流去写一段内容:
FileWriter类
FileWriter fw=new FileWriter("E:\\123.txt"); //可以看到这个文件已经被创建出来
//
FileWriter fw=new FileWriter("E:\\123.txt",true); //后面加上true就表示是续写, 不会覆盖原来内容
try {
FileWriter fw=new FileWriter("E:\\123.txt",true); //可以看到这个文件已经被创建出来
//接下来我们的任务就是通过这个对象,调用这个对象的方法,实现写的操作
fw.write("中国加油!");
// fw.flush(); //刷新一下缓冲区,告诉它,将其写到文件中去
fw.close(); //将该流关闭,一关闭就写完了(读写操作都应该养成关闭流的好习惯)
} catch (Exception e) {
e.printStackTrace();
}
File类是一个对文件进行操作的类,具体使用可百度或者api