一.定义
什么是面向对象:复杂问题 简单化,由执行者变为指挥者
面向对象的概念:
1.这是一种基于面向过程的思想
2.将功能封装进对象,强调具备了功能的对象
面向对象特点:
1.面向对象就是一种常见的思想,符合人们的思考习惯
2.面向对象的出现,将复杂的问题简单化
3.面向对象的出现,让曾经在过程中的执行者,变成了对象中的指挥者
面试题:你是怎么理解面向对象的?
1.它符合现在人们思考的一种习惯
2.它让复杂的事情简单化
3.它让我们从曾经的执行者变为了现在的指挥者
其实面试官你本身就是在用面向对象的方式思考问题,
因为以面试官您的能力而言,如果来了一个软件项目的话,您从需求分析到设计到开发到测试,
虽然肯定都能完成,但是这样特别耗费时间,所以您为了提高效率,就需要去找一些具备具备专业编程经验的人来完成项目,
我正好就是那个具备专业编程经验的对象,您只要指挥我这个对象做事情就可以了,我会给您一个非常满意的结果
至于过程您不用管。所以面试官您就是在用面向对象的方式思考问题,来提高公司的效率。
其他示例:
1.买电脑和请人一起买电脑
2.自己开发和请人开发
面向对象的三大特征:继承、封装、多态。
在以后的开发过程中,其实就是在找对象用,没有对象,就创建一个对象。
找对象,建立对象,使用对象,并维护对象的关系。
二.类和对象的关系
类:就是对现实生活中事物的描述
对象:就是这类事物,实实在在存在的个体
示例:
需求,描述小汽车
描述事物其实就是在描述事物的属性和行为
分析:1.属性 轮胎数、颜色; 2.行为 运行
定义类其实就是在定义类中的成员
成员:成员变量(属性)、成员函数(行为、方法)
所以定义类,就是在描述事物,就是在定义属性和方法,属性和行为共同成为类中的成员。
这个类中不必有主函数,因为不用独立运行。
描述汽车的类:
class Car {
//描述汽车的颜色
String color = "red";
//描述汽车的轮胎数
int num = 4;
//描述汽车的运行
void run(){
System.out.println("color="+color+"..."+"num="+num);
}
}
生成汽车的类:
public class CarTest {
public static void main(String[] args) {
//生成汽车,在Java中通过new操作符来完成
//其实就是在堆内存中产生一个实体
Car car = new Car();
//car就是一个类类型的变量,类类型的变量指向对象
//需求,将已有车的颜色改成蓝色,指挥该对象来完成,
//指挥方式:对象名.属性名
car.color = "blue";
car.run();
new Car().color = "green";//这是一个匿名对象,不过调用匿名对象的属性没有意义
new Car().run();//匿名对象调用方法,只能调用一次。
method(new Car());//匿名对象可以作为实际参数进行传递
}
//需求:汽修厂对汽车进行改装,来了的车都改为黑色、三个轮胎。
public static void method(Car car){
//将汽车的颜色改为黑色
car.color = "black";
//将汽车的轮胎数改为3个
car.num = 3;
car.run();
}
}
定义:没有名字的对象
匿名对象是对象的简化形式。
匿名对象的两种使用情况:
1.当对对象的方法仅使用一次调用时,可以使用匿名对象
2.匿名对象可以作为实际参数进行传递
注意:调用匿名对象的属性是没有意义的,因为调用完就找不到匿名对象了。
成员变量和局部变量的区别:
1.成员变量:定义在类中,作用于整个类
局部变量:定义在函数、语句、局部代码块中,只在所属大括号区域有效。
2.成员变量:存在于堆内存的对象中
局部变量:存在于栈内存的方法中
3.成员变量:随着对象的创建而存在,随着对象的消失而消失
局部变量:随着所属区域的执行而存在,随着所属区域的结束而释放
4.成员变量:有默认初始化值,可以直接参与运算
局部变量:没有默认初始化值,不能直接参与运算
三.封装
定义:指隐藏对象的属性和实现细节,仅对外提供公共访问方式
封装的好处:
1.将变化隔离
2.便于使用
3.提高重用性
4.提高安全型
封装的原则:
1.将不需要对外提供的内容都隐藏起来
2.把属性都隐藏起来,提供公共方法对其进行访问
四.private关键字
定义:私有,为权限修饰符,用于修饰类中的成员(变量、方法),
被修饰的内容在其他类中是不可以被访问的,所以私有只在本类中有效。
注意:私有仅仅是封装的一种表现形式
之所以对外提供访问方式,是因为在访问方式中加入逻辑判断等语句,对访问的数据进行操作,提高代码的健壮性。
五.构造函数
定义:构建创造对象时调用的函数
特点:1.函数名与类名相同,2.不用定义返回值类型,3.不可以写return语句
作用:给对象进行初始化
创建对象都必须要通过构造函数初始化。
一个类中如果没有定义过构造函数,那么系统会默认给该类添加一个空参数的构造函数。
如果在类中有了指定参数的构造函数,那么类中的默认构造函数就没有了。
注意:
1.默认构造函数的特点
2.多个构造函数是以重载的形式存在的
一般函数和构造函数有什么区别呢?
构造函数:对象创建时,就会调用与之对应的构造函数,对对象进行默认初始化。
对象创建时,只调用一次。
一般函数:对象创建后,需要函数功能时才调用。
对象创建后,可以被调用多次。
什么时候定义构造函数呢?
在描述事物时,该事物一存在就具备一些内容,这些内容都定义在构造函数中。
构造函数可以有多个,用于对不同的对象进行针对性的初始化。
六.构造代码块
作用:给所有对象进行初始化,对象一建立就运行,且优先于构造函数执行
和构造函数的区别:
1.构造代码块是给所有对象进行统一初始化的。
2.构造函数是给对应的对象进行初始化的。
构造代码块中定义的是不同对象共性的初始化内容,且和构造函数一样,在给对象进行初始化时执行一次。
七.this关键字
特点:this代表其所在函数所属对象的引用,换言之,this代表本类对象的引用。
简单的说,哪个对象在调用this所在的函数,this就代表哪个对象。
作用:
1.用于区分局部变量和成员变量同名的情况
2.用于构造函数间互相调用,不能用于一般函数间相互调用
应用:
当定义类中功能时,该函数内部要用到调用该函数的对象时,这时用this来表示这个对象
但凡本类功能内部使用到了本类对象,都用this表示
注意:
1.构造函数间调用只能用this
2.this只能定义在构造函数的第一行,因为初始化动作要先执行。
person(String name){
this.name = name;
}
person(String name,int age){
this(name);
this.age = age;
}
八.static关键字
作用:用于修饰成员(成员变量和成员函数),它是一个修饰符
被修饰后的成员具备以下特点:
1.随着类的加载而加载,也随着类的消失而消失,生命周期最长
2.优先于对象存在,静态先存在,对象后存在
3.被所有对象所共享,在内存中单独存在
4.可以直接被类名调用,类名.静态成员
使用注意:
1.静态方法只能访问静态成员(方法、变量)
2.静态方法中不可以写this、super关键字
3.主函数是静态的
成员变量和静态变量的区别:
1.存放位置,静态变量随着类的加载而存在于方法区中,成员变量随着对象的建立而存在于堆内存中
2.生命周期,静态变量生命周期最长,随着类的消失而消失;成员变量的生命周期随着对象的消失而消失
3.调用方式,静态变量可以被对象调用,也可以被类名调用;成员变量只能被对象调用
什么时候使用静态变量呢?
当对象中出现了共享数据时,该数据可以被静态修饰
对象中的特有数据要定义成非静态存在堆内存中
什么时候定义静态函数呢?
当功能内部没有访问到非静态数据,或者对象的特有数据,那么该功能可以定义成静态的。
静态的利与弊
利:对对象的共享数据进行单独空间的存储,节省空间,可以直接被类名调用。
弊:生命周期过长,访问出现局限性(静态虽好,只能访问静态)
九.main函数
public static void main(String[ ] args)
主函数:这是一个特殊的函数,作为程序的入口。可以被JVM所识别和调用,且格式固定(除了args可以改,其它不行)
public:代表该函数的访问权限最大
static:代表主函数随着类的加载就已经存在了
void:代表主函数没有具体的返回值
main:不是一个关键字,但是这是一个特殊的单词,可以被JVM识别的固定名称
(Sring[ ] args):主函数的参数列表,参数类型是一个数组,该数组中的元素是字符串
静态的应用:工具类,方便在任意类中复用。
public class ArrayTool {
public static void main(String[] args){
int[] arr = {1,3,5,7,9};
ArrayTool.bianLi(arr);
}
<span style="white-space:pre"> </span>//这是一个遍历数组的静态方法,可以直接被类名调用
public static void bianLi(int[] arr){
for(int x=0;x<arr.length;x++){
if(x!=arr.length-1)
System.out.print(arr[x]+",");
else
System.out.print(arr[x]);
}
}
}
十.帮助文档的制作
dos命令:javadoc -d myclass -author -version ArrayTool.java
javadoc提取的类必须是public,否则报错。
/**
这是一个可以对数组进行操作的工具类,该类提供了获取最值、排序等功能。
@author 詹彦涛
@version V1.0
*/
public class ArrayTool{
/**
空参数构造函数。
*/
private ArrayTool(){}
//获取最大值
/**
获取一个整形数组中的最大值。
@param arr 接收一个int类型的数组
*/
public static void getMax(int[] arr){
int max = arr[0];
for(int x=0;x<arr.length-1;x++){
if(arr[x]>max){
max = arr[x];
}
}
System.out.println("max="+max);
}
//获取最小值
/**
获取一个整形数组中的最小值。
@param arr 接收一个int类型的数组
*/
public static void getMin(int[] arr){
int min = arr[0];
for(int x=0;x<arr.length-1;x++){
if(arr[x]<min){
min = arr[x];
}
}
System.out.println("min="+min);
}
//位置交换功能抽取(私有化)
/**
给int数组进行位置置换。
@param arr 接收一个int类型的数组
@param a 要置换的位置
@param b 要置换的位置
*/
private static void swap(int[] arr,int a,int b){
if(arr[a]>arr[b]){
int temp = arr[a];
arr[a] = arr[b];
arr[b] = temp;
}
}
//遍历
/**
用于打印数组中的元素,打印形式是:[elment1,element2...]
@param arr 接收一个int类型的数组
*/
public static void printArr(int[] arr){
System.out.print("[");
for(int x=0;x<arr.length;x++){
if(x!=arr.length-1){
System.out.print(arr[x]+",");
}else{
System.out.println(arr[x]+"]");
}
}
}
}
十一.静态代码块
格式:static{
静态代码块中的执行语句;
}
特点,随着类的加载而执行,且只执行一次,并优先于主函数,用于给类进行初始化。
十二.对象的初始化过程
public class Person {
<span style="white-space:pre"> </span>private String name;
<span style="white-space:pre"> </span>private int age;
<span style="white-space:pre"> </span>private static String country = "CN";
<span style="white-space:pre"> </span>
<span style="white-space:pre"> </span>Person(String name,int age){
<span style="white-space:pre"> </span>this.name = name;
<span style="white-space:pre"> </span>this.age = age;
<span style="white-space:pre"> </span>}
<span style="white-space:pre"> </span>
<span style="white-space:pre"> </span>public void setAge(int age){
<span style="white-space:pre"> </span>this.age = age;
<span style="white-space:pre"> </span>}
<span style="white-space:pre"> </span>
<span style="white-space:pre"> </span>public int getAge(){
<span style="white-space:pre"> </span>return age;
<span style="white-space:pre"> </span>}
<span style="white-space:pre"> </span>
<span style="white-space:pre"> </span>public void setName(String name){
<span style="white-space:pre"> </span>this.name = name;
<span style="white-space:pre"> </span>}
<span style="white-space:pre"> </span>
<span style="white-space:pre"> </span>public String getName(){
<span style="white-space:pre"> </span>return name;
<span style="white-space:pre"> </span>}
<span style="white-space:pre"> </span>
<span style="white-space:pre"> </span>public void speak(){
<span style="white-space:pre"> </span>System.out.println(this.name+"..."+this.age);
<span style="white-space:pre"> </span>}
<span style="white-space:pre"> </span>public static void showCountry(){
<span style="white-space:pre"> </span>System.out.println("country="+country);
<span style="white-space:pre"> </span>}
<span style="white-space:pre"> </span>
<span style="white-space:pre"> </span>public static void main(String[] args){
<span style="white-space:pre"> </span>Person p = new Person("zhangsan",25);
<span style="white-space:pre"> </span>}
}
1.因为new用到了Person.class文件,所以会先找到Person.class文件并加载到内存中。
2.执行该类中的static代码块,如果有的话,给Person类进行初始化
3.在堆内存中开辟空间,分配内存地址
4.在堆内存中建立对象的特有属性,并进行默认初始化(name=null age=0 country=null)
5.对属性进行显示初始化(也就是属性初始化,如果没有赋值那么就是默认的值)
6.对对象进行构造代码块初始化
7.对对象进行对应的构造函数初始化
8.将内存地址赋给栈内存中的p变量
十三.单例设计模式
设计模式:解决某一类问题最行之有效的方法,Java中有23种设计模式。
单例设计模式:解决如何让一个类在内存中只存在一个对象的问题。
应用:比如程序的配置信息等
想要保证对象唯一性
1.为了避免其它程序过多的建立该类对象,先禁止其他程序建立该类对象
2.为了让其它程序可以访问到该类对象,可以先在本类中,自定义一个对象
3.为了方便其它程序对自定义对象的访问,可以对外提供一些访问方式
这三步用代码怎么体现呢?
1.将构造函数私有化
2.在类中创建一个本类对象
3.提供一个方法可以获取到本类对象
使用方法:
对于事物该怎么描述,还怎么描述
当需要将该事物的对象保证在内存中唯一时,就将以上的三步加上即可
单例设计模式又有两种方式来实现,一种是懒汉式,一种是饿汉式。
懒汉式代码:
public class SingleDemo {
private int num;
public void setNum(int num){
this.num = num;
}
public int getNum(){
return num;
}
private SingleDemo(){
}
private static SingleDemo sd = new SingleDemo();
public static SingleDemo getInstance(){
return sd;
}
public static void main(String[] args) {
SingleDemo s1 = SingleDemo.getInstance();
s1.setNum(15);
SingleDemo s2 = SingleDemo.getInstance();
System.out.println(s2.getNum());
}
}
开发一般使用饿汉式,因为安全、简单
在上面的示例代码,Single类一进内存,就已经创建好了对象。
懒汉式代码:
public class SingleDemo2 {
private SingleDemo2(){}
private static SingleDemo2 sd = null;
public static SingleDemo2 getInstance(){
if(sd==null){
sd = new SingleDemo2();
}
return sd;
}
}
懒汉式,对象是方法被调用时才初始化,也叫做对象的延时加载
Single类进内存,对象还没有存在,只有调用了getInstance 方法时,才建立对象。
但是上述代码不安全,有可能会建立多个对象,因此需要加入同步锁,来保证安全,因此效率低。代码如下
public class SingleDemo2 {
private SingleDemo2(){}
private static SingleDemo2 sd = null;
public static SingleDemo2 getInstance(){
if(sd==null){
synchronized(SingleDemo2.class){
if(sd==null){
sd = new SingleDemo2();
}
}
}
return sd;
}
}
十四.继承
继承的作用:
1.提高了代码的复用性
2.让类与类之间产生了关系,有了这个关系,才有了多态的特性
注意:
千万不要为了获取其它类的功能,简化代码而继承。
必须是类与类之间有所属关系才可以继承。所属关系is a
Java语言中,Java只支持单继承,不支持多继承。
因为多继承容易来来隐患,当多个父类中定义了相同名字的功能,虽然功能内容可能不同,
但是子类对象不确定要运行哪一个。
虽然不支持多继承,但是Java用另外一种体现形式来完成这种表示,那就是多实现。
Java支持多层继承,也就是一个继承体系。
如何使用一个继承体系中的功能呢?
想要使用体系,先查阅体系中父类的描述,因为父类中定义的是该体系中共性功能,
通过了解共性功能,就可以知道该体系的基本功能,那么这个体系已经可以基本使用了。
在具体调用时,要创建子类的对象,为什么呢?
1.因为有可能父类不能创建对象
2.创建子类对象可以使用更多的功能,包括基本的也包括特有的。
简单一句话:查阅父类功能,创建子类对象使用功能。
class Person{
String name;
int age;
}
class Student extends Person{
void study(){
System.out.println("good study");
}
}
class Worker extends Person{
void work(){
System.out.println("good work");
}
}
子父类中成员的特点
类中成员有:1.变量,2.函数,3.构造函数
变量的特点:
如果子类中出现非私有的同名成员变量时,子类要访问本类中的同名变量,用this;
子类如果要访问父类中的同名变量,用super。
super的使用和this几乎一致,this代表的是本类对象的引用,super代表的是父类对象的引用。
class Fu{
int num = 4;
}
class Zi extends Fu{
int num = 5;
void show(){
System.out.println(num);
}
}
class ExtendsDemo2
{
public static void main(String[] args)
{
Zi z = new Zi();
z.show();
//System.out.println(z.num+"...."+z.num);
}
}
子父类中函数的特点
当子类出现和父类一模一样的函数,当子类对象调用该函数,会运行子类函数的内容,
如同父类函数被覆盖一样,这种情况是函数的另一个特性,重写(覆盖)。
当子类继承父类,沿袭了父类的功能,在子类中,虽然子类具备了该功能,但是功能的内容却和父类不一致,
这时,没有必要重新定义新功能,而是使用覆盖,保留父类的功能定义,并重写功能内容。
覆盖的注意事项:
1.子类覆盖父类,必须保证子类权限大于等于父类权限,才可以覆盖,否则编译失败。
2.静态只能覆盖静态。
记住:
重载:只看同名函数的参数列表
重写:子父类方法要一模一样
class Fu{
void show(){
System.out.println("Fu show");
}
void speak(){
System.out.println("vb");
}
}
class Zi{
void show(){
System.out.println("Zi show");
}
public void speak(){
System.out.println("java");
}
}
class ExtendsDemo3
{
public static void main(String[] args)
{
Zi z = new Zi();
z.speak();
}
}
子父类中的构造函数
在对子类对象进行初始化时,父类的构造函数也会运行。
因为子类的构造函数默认第一行有一条隐式的语句super()。
super():这条语句会访问父类中空参数的构造函数,而且子类中所有的构造函数默认第一行都是super()。
为什么子类一定要访问父类中的构造函数呢?
因为父类中数据子类可以直接获取,所以子类对象在建立时,需要查看父类是如何对这些数据进行初始化的。
所以子类在对象初始化时,要先访问一下父类中的构造函数。
如果要访问父类中指定的构造函数,可以通过手动定义super语句的方式来指定。
注意:super语句一定要放在子类构造函数的第一行。
结论:
1.子类所有的构造函数,默认都会访问父类中空参数的构造函数,因为子类每一个构造函数内的第一行都有一句隐式super()。
2.当父类中没有空参数的构造函数时,子类必须手动通过super语句来指定要访问的构造函数。
3.当然,子类的构造函数第一行也可以手动指定this语句来访问本类中的构造函数。
子类中至少会有一个构造函数会访问父类中的构造函数。
class Fu{
int num;
Fu(){
num = 60;
System.out.println("Fu run");
}
Fu(int x){
System.out.println("Fu...."+x);
}
}
class Zi extends Fu{
int num = 50;
Zi(){
//super();
//super(3);
System.out.println("Zi run");
}
Zi(int x){
this();
//super();
//super(3);
System.out.println("Zi...."+x);
}
}
public class Test
{
public static void main(String[] args)
{
Zi z = new Zi(4);
System.out.println();
}
}
十五.final关键字
定义:最终。是一个修饰符。
特点概述:
1.可以修饰类、函数、变量
2.被final修饰的类不可以被继承。为了避免被继承,被子类复写。
3.被final修饰的方法不可以被覆写。
4.被final修饰的变量是一个常量且只能赋值一次,既可以修饰成员变量,也可以修饰局部变量。
当在描述事物时,一些数据的出现值是固定的,那么这时为了增强阅读性,都给这些值起个名字,方便于阅读。
而这些值不需要改变,所以加上final修饰,作为常量,常量的书写规范所有字母都大写,如果由多个单词组成,单词间通过_连接。
5.内部类定义在类中的局部位置上时,只能访问该局部被final修饰的局部变量。
class Demo{
final int X = 3;
public static final double MY_PAI = 3.14;
final void show1(){
}
}
class SubDemo extends Demo{
//因为show1被final修饰,所以无法被复写
}
十六.抽象类
为什么会出现抽象类?
当多个类中出现相同功能,但是功能主体不同的情况时,这时可以进行向上抽取,但是只抽取功能定义,而不抽取功能主体。
抽象:即看不懂
抽象类的特点:
1.抽象方法一定在抽象类中。
2.抽象方法和抽象类都必须被abstract关键字修饰。
3.抽象类不可以用new创建对象,因为调用抽象方法没有意义。
4.抽象类中的抽象方法如果要被使用,必须由子类复写其所有的抽象方法后,建立子类对象调用。
如果子类只覆盖了部分抽象方法,那么该子类还是一个抽象类。
抽象类和一般类没有太大的不同
该如何描述事物,就如何描述事物,只不过,该事物出现了一些看不懂的东西。
这些不确定的部分,也是该事物的功能,需要明确出现,但是无法定义主体。通过抽象方法来表示。
抽象类比一般类多了个抽象函数,就是在类中可以定义抽象方法,抽象类不可以被实例化。
当然,抽象类中也可以没有抽象函数,只有一般函数,但是这种情况比较少见。
特殊:抽象类中可以不定义抽象方法,这样做仅仅是不让该类建立对象。
abstract class Student{
abstract void study();
void sleep(){
System.out.println("躺着");
}
}
class BaseStudent extends Student{
void study(){
System.out.println("base study");
}
}
class AdvStudent extends Student{
void study(){
System.out.println("adv study");
}
}
class Abstract
{
public static void main(String[] args)
{
new BaseStudent().study();
}
}
抽象类练习:
/*
假如我们在开发一个系统时,需要对员工进行建模,员工包含3个属性;
姓名、工号、工资。经理也是员工,除了含有员工的属性外,另外还有一个奖金属性。
请使用继承的思想设计出员工类和经理类。
要求类中必须提供必要的方法进行属性访问。
*/
//员工类和经理类的抽取的共性类
abstract class Employee{
private String name;
private String id;
private double pay;
Employee(String name,String id,double pay){
this.name = name;
this.id = id;
this.pay = pay;
}
public void setName(String name){
this.name = name;
}
public String getName(){
return name;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public double getPay() {
return pay;
}
public void setPay(double pay) {
this.pay = pay;
}
public abstract void work();
}
//员工类
class Pro extends Employee{
Pro(String name,String id,double pay){
super(name,id,pay);
}
public void work(){
System.out.println("pro work");
}
}
//经理类
class Manager extends Employee{
private double bonus;
Manager(String name,String id,double pay,double bonus){
super(name,id,pay);
this.bonus = bonus;
}
public double getBonus() {
return bonus;
}
public void setBonus(double bonus) {
this.bonus = bonus;
}
public void work(){
System.out.println("manager work");
}
}
public class AbstractTest {
public static void main(String[] args) {
Pro p = new Pro("zhangsan","001",100.88);
p.setPay(888.88);
System.out.println(p.getPay());
}
}
十七.模板方法设计模式
1.需求:获取一段程序运行的时间。
2.原理:获取程序开始和结束的时间并相减
3.获取时间:System.currentTimeMillis( );
4.这种方式,叫模板方法设计模式
5.模板方法设计模式:在定义功能时,功能的一部分是确定的,但是有一部分是不确定的,
而确定的部分在使用不确定的部分,那么这时就将不确定的那部分暴露出去,由该类的子类去完成。
abstract class GetTime{
public void getTime(){
long start = System.currentTimeMillis();
runCode();
long end = System.currentTimeMillis();
System.out.println("毫秒:"+(end-start));
}
public abstract void runCode();
}
class SubTime extends GetTime{
public void runCode(){
for(int x=0;x<500;x++){
System.out.println(x);
}
}
}
public class TemplateDemo {
public static void main(String[] args) {
SubTime sb = new SubTime();
sb.getTime();
}
}
十八.接口
接口的特点:
1.接口是对外暴露的规则
2.接口是程序的功能扩展
3.接口可以用来多实现。
4.类与接口之间是实现关系,而且类可以继承一个类的同时实现多个接口。
5.接口与接口之间可以有继承关系。
初期理解,可以认为是一个特殊的抽象类,但是这个抽象类中的方法都是抽象的,那么该类可以通过接口的形式来表示。
class用于定义类,interface用于定义接口。
接口定义时,格式特点:
1.接口中常见定义:常量,抽象方法。
2.接口中的成员都有固定修饰符
常量:public static final
方法:public abstract
记住:
1.接口中的成员都是public的。
2.接口是不可以创建对象的,因为有抽象方法,需要被子类实现,
子类对接口中的抽象方法全部覆盖后,才可以实例化,否则子类是一个抽象类。
3.接口可以被类多实现,也是对多继承不支持的转换形式,Java支持多实现。
interface Inter{
public static final int NUM = 3;
public abstract void show();
}
interface InterA{
public abstract void show();
}
class Demo{
public void function(){}
}
class Test extends Demo implements Inter,InterA{
//虽然Inter和InterA里都有show方法,但是都是抽象方法,没有方法体,所以可以一次全覆盖。
public void show(){}
}
class InterfaceDemo
{
public static void main(String[] args)
{
Test t = new Test();
System.out.println(t.NUM);
System.out.println(Test.NUM);
System.out.println(Inter.NUM);
}
}
十九.多态
多态:可以理解为事物存在的多种体现形态。
人:男人、女人
动物:猫、狗
猫 x = new 猫(); 动物 x = new 猫();
多态的体现
父类的引用指向了自己的子类对象。
父类的引用也可以接受自己的子类对象。
多态的前提
必须是类与类之间有关系,要么继承,要么实现。
同时还有一个前提:存在覆盖。
多态的好处
多态的出现大大的提高了程序的扩展性
多态的弊端
提高了扩展性,但是只能使用父类的引用访问父类中的成员。
<span style="font-size:14px;">abstract class Animal{
abstract void eat();
}
class Cat extends Animal{
public void eat(){
System.out.println("吃鱼");
}
public void catchMouse(){
System.out.println("抓老鼠");
}
}
class Dog extends Animal
{
public void eat(){
System.out.println("吃骨头");
}
public void kanJia(){
System.out.println("看家");
}
}
class DuoTaiDemo
{
public static void main(String[] args)
{
<span style="white-space:pre"> </span>Animal a = new Cat();
function(a);
}
public static void function(Animal a){
a.eat();
}
}</span>
Animal a = new Cat( );//这句话中的a类型得到了提升,向上转型。
但是这时如果想要调用猫特有方法时,如何操作呢?
这个时候要将父类的引用,转成子类类型,向下转型。
Cat c = (Cat) a;
但是千万不能出现这样的操作,就是将父类对象转成子类类型。
我们能转换的是父类的引用指向自己的子类对象时,该引用可以被提升,也可以被强制转换。
记住:多态自始至终都是子类在做着变化!
在多态中,(非静态)成员函数的特点:
在编译时期:参阅引用型变量所属的类中是否有调用的方法,如果有编译通过,如果没有编译失败。
在运行时期:参阅对象所属的类中是否有调用的方法。
简单总结就是:成员函数在多态调用时,编译看左边,运行看右边。
动态绑定在调用函数的对象上。
在多态中,成员变量(静态成员函数)的特点:
无论编译和运行,都参考左边(引用型变量所属的类)。
静态绑定在静态所述的类中。
示例一:
class Fu{
int num = 5;
void Method1(){
System.out.println("fu_method1");
}
void Method2(){
System.out.println("fu_method2");
}
static void Method4(){
System.out.println("fu_method4");
}
}
class Zi extends Fu{
int num = 8;
void Method1(){
System.out.println("zi_method1");
}
void Method3(){
System.out.println("zi_method3");
}
static void Method4(){
System.out.println("zi_method4");
}
}
class DuoTaiDemo2{
public static void main(String[] args)
{
/*非静态成员函数:编译时看左边,运行时看右边。
Fu f = new Zi();
f.Method1();
f.Method2();
//f.Method3();*/
/*成员变量:编译运行都看左边。
Fu f = new Zi();
System.out.println(f.num);
Zi z = new Zi();
System.out.println(z.num);*/
/*静态成员函数:编译运行都看左边。
Fu f = new Zi();
f.Method4();
Zi z = new Zi();
z.Method4();*/
}
}
实例二:
/*
需求:
电脑运行示例,电脑运行基于主板,扩展基于PCI插槽。
*/
interface PCI{
public abstract void open();
public abstract void close();
}
class MainBoard{
public void run(){
System.out.println("MainBoard run");
}
public void usePCI(PCI p){
if(p!=null){
p.open();
p.close();
}
}
}
class NetCard implements PCI{
public void open(){
System.out.println("NetCard open");
}
public void close(){
System.out.println("NetCard close");
}
}
class SongCard implements PCI{
public void open(){
System.out.println("SongCard open");
}
public void close(){
System.out.println("NetCard close");
}
}
class DuoTaiDemo5
{
public static void main(String[] args)
{
MainBoard mb = new MainBoard();
mb.run();
mb.usePCI(new NetCard());
mb.usePCI(new SongCard());
}
}
二十.内部类
内部类的访问规则:
1.内部类可以直接访问外部类中的成员,包括私有。
之所以可以直接访问,是因为内部类中持有了一个外部类的引用,格式:外部类名.this
2.外部类要访问内部类,必须建立内部类对象。
内部类的访问格式:
1.当内部类定义在外部类的成员位置上,而且非私有,可以在外部其他类中,直接建立内部类对象。
格式:外部类名.内部类名 变量名 = 外部类对象.内部类对象。
Outer.Inner in = new Outer().new Inner();
2.当内部类定义在外部类的成员位置上,就可以被成员修饰符所修饰
比如:private:将内部类在外部类中进行封装。
static:内部类就是具备static的特性。
当内部类被static修饰后,只能直接访问外部类中的static,出现了访问局限。
在外部其他类中,如何直接访问static修饰的内部类的非静态成员呢?
new Outer.Inner().function;
在外部其他类中,如何直接访问static内部类的静态成员呢?
Outer.Inner.function();
注意:当内部类中定义了静态成员,该内部类必须是static的。
当外部类中的静态方法访问内部类时,内部类也必须是静态的。
内部类的由来:
当描述事物时,事物的内部还有事物,该事物用内部类来描述,因为内部事务在使用外部事物的内容。
<span style="font-size:14px;">class Outer{
private static int x = 3;
static class Inner{//静态内部类
static void function(){
System.out.println("Inner:"+x);
}
}
static class Inner2{
void show(){
System.out.println("inner2 show");
}
}
public static void method(){
//Inner.function();
new Inner2().show();
}
}
class InnerClassDemo2
{
public static void main(String[] args)
{
Outer out = new Outer();
out.method();
/*直接访问内部类中的成员
Outer.Inner in = new Outer().new Inner();
in.function();
*/
}
}</span>
内部类定义在局部时:
1.不可以被成员修饰符修饰
2.可以直接访问外部类中的成员,因为还持有外部类中的引用。
但是不可以访问它所在的局部中的变量,只能访问被final修饰的局部变量。
class Outer{
int x = 3;
void method(final int a){
final int y = 4;
class Inner{
void function(){
System.out.println(a);
}
}
new Inner().function();
}
}
class InnerClassDemo3
{
public static void main(String[] args)
{
new Outer().method(5);
}
}
匿名内部类:
1.匿名内部类其实就是内部类的简写格式
2.定义匿名内部类的前提:内部类必须是继承一个类或者实现接口
3.匿名内部类的格式:new 父类或者接口(){ 定义子类内容 }
4.其实匿名内部类就是一个匿名子类对象
5.匿名内部类中定义的方法最好不要超过两个。
abstract class AbsDemo{
abstract void show();
}
class Outer
{
int x =3;
/*内部类
class Inner extends AbsDemo{
void show(){
System.out.println("show:"+x);
}
void abc(){
System.out.println("haha");
}
}
*/
public void function(){
//new Inner().show();
//AbsDemo a = new Inner;
/*
Inner in = new Inner();
in.show();
in.abc();
*/
AbsDemo d = new AbsDemo(){
void show(){
System.out.println("x="+x);
}
void abc(){
System.out.println("haha");
}
};
d.show();
//d.abc();编译失败,因为建立了多态,父类中没有abc()方法。
}
}
class InnerClassDemo4
{
public static void main(String[] args)
{
new Outer().function();
}
}
二十一.异常
定义:就是程序在运行时出现不正常的情况。
异常的由来:问题也是现实生活中一个具体的事物,也可以通过Java类的形式进行描述,并封装成对象。
其实就是Java对不正常情况进行描述后的对象体现。
对于问题的划分有两种,一种是严重的问题,一种是非严重的问题。
对于严重的,Java通过Error类进行描述。对于Error一般不编写针对性的代码对其进行处理。
对于非严重的,Java通过Exception类进行描述,对于Exception可以使用针对性的处理方式进行
处理。
Throwable
|--Error
|--Exception
|--RuntimeException
异常的处理
Java提供特有的语句进行处理。
try{
需要被监测的代码。
}catch(异常类 变量){
处理异常代码;(处理方式)
}finally{
一定会执行的语句。
}
对捕获到的异常对象进行常见方法的操作
1.getMessage():获取异常信息
2.printStackTrace():获取异常名称,异常信息,异常出现的位置。
class Demo{
int div(int a,int b){
return a/b;
}
}
class ExceptionDemo
{
public static void main(String[] args)
{
Demo d = new Demo();
try{
int x = d.div(4,1);
System.out.println("x="+x);
}catch(Exception e){ //Exception e = new ArithmeticException();
System.out.println("除零啦!");
System.out.println(e.getMessage());//异常信息
System.out.println(e.toString());//异常名称:异常信息
e.printStackTrace();//异常名称,异常信息,异常出现的位置。
/*JVM默认的异常处理机制,就是在调用printStackTrace方法,
打印异常的堆栈的跟踪信息。*/
}
System.out.println("Over");
}
}
在函数上声明异常
便于提高安全性,让调用者进行处理,不处理则编译失败。
对多异常的处理:
1.声明异常时,建议声明更为具体的异常,这样处理的可以更具体。
2.对方声明几个异常,就应该有几个catch块,不要定义多余的catch块,
如果多个catch块中的异常出现继承关系,父类异常catch块放在最后。
建议在进行catch处理时,catch中一定要定义具体处理方式。
不要简单定义一句:e.printStackTrace(),也不要简单的书写一条打印语句。
class Demo{
//在功能上通过throws关键字声明了该功能可能会出现的问题。
int div(int a,int b)throws ArithmeticException,ArrayIndexOutOfBoundsException{
int[] arr = new int[a];
System.out.println(arr[4]);
return a/b;
}
}
class ExceptionDemo2
{
public static void main(String[] args)
{
Demo d = new Demo();
try{
int x = d.div(4,1);
System.out.println("x="+x);
}catch(ArithmeticException e){
System.out.println(e.toString());
System.out.println("被零除了!");
}catch(ArrayIndexOutOfBoundsException e){
System.out.println(e.toString());
System.out.println("角标越界了!");
}catch(Exception e){
System.out.println(e.toString());
}
System.out.println("Over");
}
}
为什么要自定义异常?
因为项目中会出现特有问题,而这些问题并未被Java所描述并封装对象,
所以对于这些特有的问题可以运行Java对问题进行封装的思想,将特有问题,进行自定义的异常封装。
当在函数内部出现throw抛出异常对象,那么就必须要给出对应的处理动作。
要么在函数内部try catch处理,要么在函数上声明让调用者处理。
一般情况下,函数内出现异常,函数上需要声明。
如何自定义异常信息呢?
因为父类中已经把异常信息的操作都已经完成了。
所以子类只要在构造时,将异常信息传递给父类,通过super语句。
那么就可以直接通过getMessage方法获取自定义的异常信息了。
自定义异常:必须是自定义类继承Exception。
继承Exception原因:
异常体系有一个特点,因为异常类和异常对象都可以被抛出。
他们具备可抛性,这个可抛性是throwable这个体系所独有特点。
只有这个体系中的类和对象,才可以被throw和throws操作。
throw和throws的区别?
1.throw使用在函数内,throws使用在函数上。
2.throws后面跟的是异常类,并且可以跟多个,类与类之间用逗号隔开,throw后面跟的是异常对象。
class FuShuException extends Exception{
<span style="white-space:pre"> </span>FuShuException(String msg){
<span style="white-space:pre"> </span>super(msg);
<span style="white-space:pre"> </span>}
}
class Demo{
<span style="white-space:pre"> </span>int div(int a,int b)throws FuShuException{
<span style="white-space:pre"> </span>if(b<0){
<span style="white-space:pre"> </span>throw new FuShuException("除数为负数了!");
<span style="white-space:pre"> </span>}
<span style="white-space:pre"> </span>return a/b;
<span style="white-space:pre"> </span>}
}
class ExceptionDemo
{
<span style="white-space:pre"> </span>public static void main(String[] args)
<span style="white-space:pre"> </span>{
<span style="white-space:pre"> </span>Demo d = new Demo();
<span style="white-space:pre"> </span>try{
<span style="white-space:pre"> </span>d.div(5,-1);
<span style="white-space:pre"> </span>}catch(FuShuException e){
<span style="white-space:pre"> </span>e.printStackTrace();
<span style="white-space:pre"> </span>}
<span style="white-space:pre"> </span>System.out.println("over");
<span style="white-space:pre"> </span>}
}
Exception中有一个特殊的子类异常:RuntimeException(运行时异常)。
1.如果在函数内抛出了该异常,函数上可以不用声明,编译一样通过。
2.如果在函数上声明了该异常,调用者可以不用进行处理,编译一样通过。
之所以不用在函数上声明,是因为不需要调用者处理。
当该异常发生后,希望程序停止,是因为在运行时,出现了无法继续运算的情况,希望停止程序后,对代码进行修正。
自定义异常时,如果由于该异常的发生,无法再继续进行运算,就让自定义异常继承RuntimeException。
对于异常分为两种:
1.编译时被检测的异常。
2.编译时不被检测的异常(运行时异常,RuntimeException及其子类)
class FuShuException1 extends RuntimeException{
FuShuException1(String msg){
super(msg);
}
}
class Demo1{
int div(int a,int b){
if(b==0){
throw new ArithmeticException();
}
if(b<0){
throw new FuShuException1("除数为负数了!");
}
return a/b;
}
}
public class Exception2 {
public static void main(String[] args) {
Demo1 d = new Demo1();
d.div(4,-1);
System.out.println("over");
}
}
示例:毕老师用电脑讲课。 名词抽取法:老师、讲课
开始思考上课中可能出现的异常、
比如:电脑蓝屏
电脑冒烟
要对问题进行描述,封装成对象。
电脑蓝屏,可以重启后继续讲课。
但是电脑冒烟后,出现课时无法继续进行的情况,且课时计划无法完成。
class LanPingException extends Exception{
LanPingException(String msg){
super(msg);
}
}
class MaoYanException extends Exception{
MaoYanException(String msg){
super(msg);
}
}
class NoPlanException extends Exception{
NoPlanException(String msg){
super(msg);
}
}
class Computer{
int state = 3;
public void run()throws LanPingException,MaoYanException{
if(state==2){
throw new LanPingException("电脑蓝屏!");
}
if(state==3){
throw new MaoYanException("电脑冒烟!");
}
System.out.println("电脑运行");
}
public void reset(){
state = 1;
System.out.println("电脑重启");
}
}
class Teacher{
private String name;
private Computer comp;
public String getName(){
return name;
}
Teacher(String name){
this.name = name;
comp = new Computer();
}
public void teach()throws NoPlanException{
try{
comp.run();
}catch(LanPingException e){
System.out.println(e.toString());
comp.reset();
}catch(MaoYanException e){
System.out.println(e.toString());
test();
throw new NoPlanException("课时无法继续进行!");
}
System.out.println("开始讲课!");
}
public void test(){
System.out.println("做练习!");
}
}
public class ExceptionTest {
public static void main(String[] args) {
Teacher t = new Teacher("毕老师");
try {
t.teach();
} catch (NoPlanException e) {
System.out.println("换老师或者放假!");
}
}
}
finally代码块:定义一定执行的代码,通常用于关闭资源。
try catch的几种格式:
第一种
try{
}catch(){
}
第二种
try{
}catch(){
}finally{
}
第三种
try{
}finally{
}
记住一点:catch是用于处理异常的,
如果没有catch就代表异常没有被处理过,如果该异常是编译时异常,那么必须声明。
异常在子父类覆盖中的体现:
异常在子父类覆盖中的体现:
1.子类在覆盖父类时,如果父类的方法抛出异常,那么子类的覆盖方法,只能抛出父类的异常或者该异常的子类。
2.如果父类方法抛出了多个异常,那么子类在覆盖该方法时,只能抛出父类异常的子集。
3.如果父类或者接口的方法中没有异常抛出,那么子类在覆盖方法时,也不可以抛出异常。
如果子类方法发生了异常,就必须要进行try处理,绝对不能抛。
包与包之间进行访问,被访问的包中的类以及类中的成员,需要public修饰。
不同包中的子类还可以直接访问父类中被protected权限修饰的成员。
包与包之间可以使用的权限只有两种:public protected。
public protected defaultprivate
同一类中 ok ok ok ok
同一包中 ok ok ok
不同包中(子类) ok ok
不同包中 ok
不同包中的子类还可以直接访问父类中被protected权限修饰的成员。
包与包之间可以使用的权限只有两种:public protected。
public protected defaultprivate
同一类中 ok ok ok ok
同一包中 ok ok ok
不同包中(子类) ok ok
不同包中 ok