title: Day04-Java面向对象下
date: 2020-05-29 11:00:03
author:子陌
面向对象
内部类
1、概述
- 将一个类定义在另一个类的里面,对里面那个类就称为内部类(内置类,嵌套类)。
- 访问特点
- 内部类可以直接访问外部类中的成员,包括私有成员。
- 而外部类要访问内部类中的成员必须要建立内部类的对象。
class Outer{
private int num = 3;
// 内部类
class Inner{
void show(){
System.out.println("show run..." + num);
}
}
public void method(){
Inner in = new Inner();
in.show();
}
}
// 修饰符
class Outer1{
private static int num = 3;
// 静态内部类
static class Inner{
void show(){
System.out.println("show run..." + num);
}
static void show1(){
System.out.println("show1 run..." + num);
}
}
}
// --- >> 生成 Outer.class、 Outer$Inner.class
class InnerClassDemo{
Outer out = new Outer();
out.method(); // show run...3
// 直接访问外部类中的内部类中的成员
Outer.Inner in = new Outer().new Inner();
in.show();
// 如果内部类是静态的,相当于外部类,一加载就存在
Outer1.Inner in = new Outer1.Inner();
in.show();
// 如果内部类是静态的,成员是静态的
Outer.Inner.show1();
}
一般用于类的设计:分析事物时,发现该事物描述中还有事物,而且这个事物还在访问被描述事物的内容。这时就是还有的事物定义成内部类来描述
注意事项:如果内部类存在静态成员,该内部类必须是静态修饰
2、内部类访问外部类成员
/*
* 为什么内部类能直接访问外部类中的成员?
* 因为内部类持有了外部类的引用。 外部类名.this
*/
class Outer{
int num = 3;
class Inner{
int num = 4;
void show(){
int num = 5;
System.out.println(num); // 5
System.out.println(this.num); // 4
System.out.println(Inner.this.num); // 5
System.out.println(Outer.this.num); // 3
}
}
void method(){
new Inner().show();
}
}
class InnerClassDemo{
public static void main(String[] args){
new Outer().method();
}
}
3、内部类-局部内部类
局部内部类在局部位置上只能访问局部中被final修饰的局部变量
/*内部类可以存放在局部位置上*/
class Outer{
int num = 3;
Object method(final int zz){
int x = 9;
final int x1 = 10; // final 修饰完终身为10 不会变化
// 局部内部类
class Inner{
void show(){
System.out.println("show ... " + num + x); // err 无法访问
System.out.println("show ... " + num + x1); // ok 修饰为最终的,如果不是final变量,方法体结束,变量释放,对象还存在
System.out.println("show ... " + num + zz); // 如果不加final 也不能访问
}
}
Inner in = new Inner();
in.show();
return in; // 假设我们返回对象,如果不是最终的,变量结束就会释放,内容修改,final就是常量
}
// Inner in = new Inner(); // 无法在局部代码块外访问
}
class InnerClassDemo{
public static void main(String[] args){
new Outer().method();
}
}
4、匿名内部类
- 就是内部类的简写
- 前提:
- 内部类必须继承或者实现一个外部类或者接口
- 格式:
- new 父类or接口() {子类内容}
- 前提:
abstract class Demo{
abstract void show();
}
class Outer{
int num = 4;
/*class Inner {
void show(){
int num = 5;
System.out.println(num); // 5
}
}*/
void method(){
// new Inner().show();
// 匿名内部类 new父类
new Demo(){
// new Demo 子类对象
void show(){
System.out.println("hhhhhhhhhhhhhhh" + num);
}
// void hh(){} // ok
}.show();
}
}
abstract interface Inter{
public void hh1();
public void hh2();
}
class InnerClassDemo{
public static void main(String[] args){
new Outer().method();
hhh(new Inter(){
public void hh1(){
}
public void hh2(){
}
});
}
public static void hhh(Inter in){
in.hh1();
in.hh2();
}
}
通常使用场景:
当函数参数是接口类型时,而且接口中的方法不超过三个,可以用匿名内部类作为实际参数传递
对象初始化过程:
class Fu{
int num = 8;
Fu(){
// 此处调用super 是Object
// 显示初始化
// 构造代码块初始化
System.out.println("fufuff");
show();
}
void show(){
System.out.println("fu hehe" + num); // 被覆盖
}
}
class Zi extends Fu{
int num = 9;
{
System.out.println("construct code....." + num);
num = 10;
}
Zi(){
// super(); // 这边优先构建父类函数 此时 num = 0; 默认隐藏自动执行
// 显示初始化 num=9,在父类构造之后
// 构造代码块初始化
System.out.println("haha....." + num);
}
void show(){
System.out.println("zi hehe.." + num);
}
}
class Test{
public static void main (String[] args){
/* 执行流程:
* 1: 进Zi()构造函数
* 2: 先执行父类构造函数Fu()
* 3: 父类show()方法被覆盖,执行子类,此时num默认值为0
* 4: 进行显示初始化,num = 9
* 5: 然后进行构造代码块初始化语句 输出num = 9,重新修改为num = 10
* 6: 输出子类num = 10
*/
Zi zi = new Zi(); // 1.fufuff 2.zi hehe..0 3.construct code...9 4.haha.....10
}
}
练习:
class TD{
int y = 6;
class Inner{
static int y = 3; // 这边会编译失败,内部类不能定义静态成员
void show(){
System.out.println(y);
}
}
}
class TC{
public static void main(String[] args){
TD.Inner ti = new TD().new Inner();
ti.show();
}
}
异常处理
- 概念:Java通过面向对象思想将问题封装成了对象,用异常类对其进行描述。将正常流程的代码和问题处理代码分离,提高阅读性。
- 在Java中用类的形式对不正常的清空进行描述和封装对象,描述不正常情况的类,称之为异常类。
异常:在运行时期发生的不正常的清空。。。。
1、异常的体系(两大类)
-
Throwable:
- Error(不可处理)
- 通常出现重大问题:如运行的类不存在或者内存溢出等。
- 不编写针对代码对其处理
- 特点:是由JVM抛出的严重性问题,直接修改程序
- Exception(可以处理)
- 在运行时运行出现的一些情况,可以通过
try catch finally
- 在运行时运行出现的一些情况,可以通过
- Error(不可处理)
-
Exception和Error的子类名都是以父类名作为后缀
该体系特点就在于Throwable及其所有子类都具有可抛性(通过两个关键字throws,throw)
class Demo{
public int method(int[] arr, int index){
if(arr == null){
throw new NullPointerException("数组引用不能为空,你怎么学的!");
}
if(index >= arr.length){
throw new ArrayIndexOutOfBoundsException("大兄弟,数组下标越界了,你疯了:" + index);
}else if(index >= arr.length){
// 负数脚标用越界抛出异常不太合理,java没提供方法,我们可以自己写,封装成对象
throw new ArrayIndexOutOfBoundsException("大兄弟,数组下标负了,你真疯了:" + index);
}
return arr[index];
}
}
class ExceptionDemo{
public static void main(String[] args){
int[] arr = new int[3];
Demo d = new Demo();
d.method(arr, 30);
}
}
2、自定义异常
java没提供异常方法,我们可以自己写,通过异常封装思想,封装成对象。如负数脚标。
注意:如果让一个类成为异常类,必须要继承异常体系,只有具备异常体系才具有可抛性。
自定义异常时:要么继承Exception,要么继承RuntimeException。
// 异常处理声明
// class FuArrayIndexException extends RuntimeException{ // 如果是该继承则不进行检测,无需throws FuArrayIndexException
class FuArrayIndexException extends Exception{
FuArrayIndexException(){
}
FuArrayIndexException(String msg){
super(msg);
}
}
class Demo{
// 声明抛出 public int method(int[] arr, int index) throws FuArrayIndexException{} 同时调用main也要声明丢给虚拟机
public int method(int[] arr, int index) throws FuArrayIndexException{
if(index >= arr.length){
throw new ArrayIndexOutOfBoundsException("大兄弟,数组下标越界了,你疯了:" + index);
}else if(index < 0){
// 负数脚标用越界抛出异常不太合理,java没提供方法,我们可以自己写,封装成对象
//throw new ArrayIndexOutOfBoundsException("大兄弟,数组下标负了,你真疯了:" + index);
throw new FuArrayIndexException();
throw new FuArrayIndexException("大兄弟,数组下标负了,你真疯了:" + index);
}
return arr[index];
}
}
class ExceptionDemo{
public static void main(String[] args) throws FuArrayIndexException{
int[] arr = new int[3];
Demo d = new Demo();
d.method(arr, 30);
}
}
3、异常的分类:
-
编译时被检测异常:只要算Exception和其子类都是,除了特殊子类RuntimeException体系
出现这种问题:可以进行针对性的处理
-
编译时不检测异常(运行时异常):就是Exception中的RuntimeException和其子类
出现这种问题:这种问题发生,无法让功能继续进行,更多是调用原因或引发内部状态导致改变,这种问题一般不处理。
运行时,让调用者调用时程序强制停止,让调用者对代码进行修正
-
throw和throws的区别:
-
throws使用在函数上
throw使用在函数内
-
throws抛出的是类异常,可以抛出多个,用逗号隔开。
throw抛出的是异常对象
-
4、异常处理的捕捉形式:
这是可以对异常进行针对性处理的方式
具体格式为try{需要被检测异常的代码} catch(异常类 变量){处理异常的代码} finally{一定会被执行的代码 }
class FuArrayIndexException extends Exception{
FuArrayIndexException(){
}
FuArrayIndexException(String msg){
super(msg);
}
}
class Demo{
public int method(int[] arr, int index) throws FuArrayIndexException{
if(arr == null)
throw new NullPointerException("没有任何数组实体")
if(index < 0){
throw new FuArrayIndexException("数组下标负了:" + index);
return arr[index];
}
}
class ExceptionDemo{
public static void main(String[] args) {
int[] arr = new int[3];
Demo d = new Demo();
try{
int num = d.method(arr, -30);
System.out.println("num = " + num);
}catch(FuArrayIndexException e){ // FuArrayIndexException e = FuArrayIndexException();
System.out.println("msg--" + e.getMessage()); // msg--数组下标负了:-30
System.out.println("string--" + e); // 默认调用e.toString() string:FuArrayIndexException:数组下标负了:-30
e.printStackTrace(); // JVM的默认异常处理机制就是调用异常对象的这个方法
System.out.println("负数脚标异常");
}catch(NullPointerException e){
System.out.println(e.toString());
}catch(Exception e){
// 接收所有异常,如果放到第一个,下面的所有异常无效,编译失败,一般不这么处理,让他挂掉发现其他问题
}// ...... cache(){}
finally{
// 通常用于关闭资源(释放)资源
// 假设cache异常中return了,那么main doing...不会执行,而finally会继续执行
System.out.println("finally....");
}
// 处理完继续运行,一定会执行的代码,除非退出虚拟机 System.exit(0);
System.out.println("main doing....");
}
}
5、异常处理的原则
-
函数内容如果抛出需要检测的异常,那么函数上必须要声明
否则必须要在函数内try-catch捕捉,否则编译失败
-
如果调用到了声明异常的函数,要么try-catch要么throws,否则编译失败
-
什么时候catch,什么时候throws
功能内容可与解决,用catch,解决不了,用throws告诉调用者,由调用者处理
-
一个功能如果有多个异常,那么调用时,必须有对应多个catch进行针对性的处理
内部有几个需要检测的异常,就抛几个异常,抛出几个,就catch几个
6、异常的注意事项
- 子类在覆盖父类的方法时,父类的方法如果抛出了异常,那么子类的方法只能抛出父类的异常或者该异常的子类
- 如果父类抛出多个异常,那么子类只能抛出父类异常的子集
- 简单理解:子类覆盖父类方法只能抛出父类的异常或者子类或者子集
- 注意:如果父类的方法没有抛出异常,那么子类覆盖时绝对不能抛
Object类
所有类的根类,是不断抽取而来的,具备着所有对象都具备的共性内容。
**equals()方法:**一般都会覆盖此方法,根据对象的特有内容,建立判断对象是否相同的依据。
class Person{
private int age;
Person(int age){
this.age = age;
}
public boolean equals(Object obj){
if(obj instanceof Person){
Person p = (Person)obj;
return age == p.age;
}
throw new RuntimeException("类型错误");
return false;
}
}
class Demo{}
class ObjectDemo{
Person p1 = new Person(20);
Person p2 = new Person(20);
Person p3 = p1;
// 未重载equals方法
System.out.println(p1 == p2); // false
System.out.println(p1.equals(p2)); // false
System.out.println(p1.equals(p3)); // true
Demo d = new Demo();
System.out.println(p1.equals(d)); // false
// 重载/覆盖equals方法
System.out.println(p1.equals(p2)); // true
}
**hashCode()方法:**一般重写equals()方法时有必要重写hashCode(),维护方法常规协定
class Person{
private int age;
Person(int age){
this.age = age;
}
hashCode(){
return age;
}
}
class ObjectDemo{
Person p1 = new Person(60);
System.out.println(p1.hashCode()); // 3c
Person p2 = new Person(29);
Class clazz1 = p1.getClass();
Class clazz2 = p2.getClass();
System.out.println(clazz1 == clazz2); // true
System.out.println(clazz1.getName()); // Person
}
**toString()方法:**放回对象的字符串形式
class Person{
private int age;
Person(int age){
this.age = age;
}
public String toString(){
// return getClass().getName() + "@"+Integer.toHexString(hashCode());
return "Person:" + age;
}
}
class ObjectDemo{
Person p1 = new Person(60);
Person p2 = new Person(29);
System.out.println(clazz1 == clazz2); // true
System.out.println(clazz1.getName()); // Person
}
包(package)
- 对类文件进行分类管理
- 给类提供多层命名(名称)空间
- 写在程序文件的第一行
- 类名的全程是:包名.类名
- 包也是一种封装形式
包名规范:所有名字都小写
编译自动生成包名:javac -d . PackageDemo.java
package packa;
class DemoA{
public void show(){
System.out.println("demo A");
}
protected void pta(){ // protected 保护的权限,只有继承才能使用
System.out.println("hahahaha");
}
}
package packb;
class DemoB extends packa.DemoA{ // 不同包中类名不能直接访问,需要加包名才可以访问
public void method(){
System.out.println("demo B");
}
}
package mytest;
class PackTestDemo{
public static void main(String[] args){
packa.DemoA d = new packa.DemoA();
d.show();
packb.DemoB b = new packb.DemoB();
b.method();
}
}
总结:包与包之间的类进行访问,被访问的包中的类必须是public的,被访问的包中的类的方法也必须是public的
-
**import关键字:**导入包中的类,不导入包中的包文件
导包的原因:为了简化类名书写
-
**包的原则:**用到哪个类,就导入哪个类。
package mytest;
// packa\DemoA.class
// packa\test\DemoAA.class
// 为了解决类的繁琐我们可以用import导入
import packa.DemoA;
// 假设我们包中有很多类可以使用 * 进行通配
// 这边只会导入DemoA不会导入DemoAA
import packa.*;
import packa.test.*;
class PackTestDemo{
public static void main(String[] args){
DemoA d = new DemoA();
d.show();
DemoB b = new DemoB();
b.method();
}
}
java打包工具:jar
- 压缩:
jar -cvf haha.jar mytest
- -c :创建新的归档文件
- -v:输出生成详细信息
- -f:指定归档文件名
- 解压缩:
jar -xvf haha.jar
- -x:解压缩
想输入jar包的内容:set path = ./haha.jar java mytest.PackTestDemo