JAVA 面向对象编程(OOP)
1、初始面向对象
面向过程思想
- 步骤清晰简单,第一步,第二步做什么
- 面向过程适合处理一些较为简单的问题
面向对象思想
- 物以类聚,分类的思维模式,思考问题首先会解决问题需要哪些分类,然后对这些分类进行单独思考,最后才对某个分类下的细节进行面向过程的思索
- 面向对象适合处理复杂的问题,适合处理需要多人协作的问题
对于描述复杂的事物,为了从宏观上把握,从整体上合理分析,我们需要使用面向对象的思路来分析整个系统,但是具体到微观操作,任需要面向过程的思路去处理
或者说面向对象是一个具体的框架,面向过程是实现框架里面方法的具体流程
什么是面向对象
- 面向对象编程(Object-Oriented Programming,OOP)
- 面向对象编程的本质是:以类的方式组织代码,以对象的形式封装数据
- 抽象
三大特性
- 封装
- 继承
- 多态
2、方法回顾和加深
方法的定义
- 修饰符: public void 、public static
- 返回类型: String、 Int、 Double ...
- break 和 return的区别: break一个是跳出循环 return一个是结束方法
- 方法名:注意命名规范,驼峰原则,做到见名知意
- 参数列表:(参数类型,参数名) ...可变长参数列表
- 异常抛出
方法的调用
- 静态的方法
- 非静态的方法
package com.oop.demo01;
public class Student {
// 1、定义一个静态方法
public static void say(){
System.out.println("我是static静态方法可以直接使用类名.方法名调用");
}
// 2、定义一个非静态方法
public void sayHello(){
System.out.println("您好我是非静态方法,需要通过类的实例化 (new 类方法)来实现调用");
}
}
package com.oop.demo01;
public class Demo02 {
public static void main(String[] args) {
// 静态方法 static
Student.say();
// 输出的结果 我是static静态方法可以直接使用类名.方法名调用
// 非静态方法(开发用的比较多)
// 实例化这个类 new
// 对象类型 对象名 = 对象值;
Student student = new Student();
student.sayHello();
// 输出结果 您好我是非静态方法,需要通过类的实例化 (new 类方法)来实现调用
}
/**
* 这是两个普通放法(void)方法之间可以直接调用
* 或者都是static 静态方法也可以通过方法名直接调用
*/
public void a (){
b();
}
public void b(){
a();
}
/**
* 但如果一个是static 一个是普通方法void则无法通过方法名进行调用的
* 因为static静态方法是和类一起加载的,他的时间片特别早,当这个类存在的时候,static方法也就存在了
*/
public static void c (){
// d();报错
}
// 类实例化之后 才存在的 所以static方法的c 不能直接调用d 因为d方法还没实例化--不存在
public void d(){
c();
}
}
形参和实参
package com.oop.demo01;
public class Demo03 {
public static void main(String[] args) {
// 实际参数和形式参数的类型要一一对应
int add = Demo03.add(1, 2);// (1, 2)实际参数
System.out.println(add);
}
public static int add(int a, int b) {//(int a,int b)形式参数
return a + b;
}
}
值传递和引用传递
package com.oop.demo01;
// 引用传递:传递的是一个对象 ,本质还是值传递
public class Demo05 {
public static void main(String[] args) {
Person person = new Person();
System.out.println(person.name); // null
Demo05.change(person);
System.out.println(person.name);
}
public static void change(Person person){
// person是一个对象:指向的是---->Person person = new Person();这是一个具体的人,可以改变属性
person.name="大威天龙";
}
}
// 一个类里面只能有一个public class 但是可以有多个class
// 定义一个Person类,有一个属性:name
class Person{
String name; //默认值为null
}
面向对象的本质=>代码理解
package com.oop.demo02;
// 学生类
public class Student {
// 一个类里面存在的只有属性和方法
// 属性:字段
String name;// null
int age;// 0
// 方法
public void study(){
System.out.println(this.name+"在学习");
}
/**
* 类是对象的模板
* 如果把我们身边的东西看作程序的话
* 我们每一个人的模板就是:
* Person-->身高,体重,年龄,国籍,性别 就是我们的属性 我们每个人都是一个对象
* 一个Person模板下可以有很多个对象 也就是有很多形形色色不同的人
* 面向对象编程的本质就是:以类的形式组织代码 ---->上面的属性和方法
* 以对象的形式封装数据 给new出来的对象进行数据封装
*/
}
package com.oop.demo02;
// 一个项目应该只有一个main方法
public class Application {
public static void main(String[] args) {
// 类:抽象的,需要实例化
// 类实例化后会返回一个自己的对象!
// stu对象就是一个Student类的具体实例!
// 同一个类可以产生不同的对象 但是他们有共同的特性(属性)
// 类是对象的模板!
// 在使用new关键字创建对象的时候除了分配内存空间之外,还会给创建好的对象进行默认的初始化并拥有初始化的值
// 以及对构造器的调用
Student stu = new Student(); // 对象创建存储在栈中 初始值string = null
Student xiaoming = new Student(); // 其他类型int、double.... = 0
System.out.println(stu.name); // 默认值null
System.out.println(stu.age); // 默认值 0
System.out.println(xiaoming.name); // 默认值 null
System.out.println(xiaoming.age); // 默认值0
stu.name="大哥";
stu.age=25;
xiaoming.name="二弟小明";
xiaoming.age=18;
System.out.println("我是"+stu.name+"今年"+stu.age);
stu.study();
System.out.println("我是"+xiaoming.name+"今年"+xiaoming.age);
xiaoming.study();
//我是大哥今年25
//大哥在学习
//我是二弟小明今年18
//二弟小明在学习
}
}
- this关键字(继承和多态时做了解)
3、对象的创建分析
使用new 关键字创建对象
使用new 关键字创建对象的时候,除了分配内存空间之外,还会给创建好的对象进行默认的初始化 以及对类中构造器的调用
new 的本质就是在调用构造方法
构造器必须掌握
类中的构造器也称为为构造方法,是在进行创建对象的时候必须要调用的。并且构造器有以下两个特点:
- 必须和类的名字相同
- 必须没有返回类型,也不能写void
构造器的理解
package com.oop.demo02;
// java ----> class
public class Person {
// 通过编译查看class文件,发现一个类就算什么都不写,他也会存在一个默认的构造器(默认的无参构造方法)public Person(){}
// 构造方法定义 1、构造方法的名字必须和类名一致 2、构造方法没有返回类型也不能写void
String name;
int age;
// 构造器的作用就是实例化初始值
// 1、使用new关键字,本质是在调用构造器
// 2、用来初始化值
// 无参构造
public Person(){// 默认的构造器 =>无参构造
}
// 有参构造:一旦定义了有参构造,无参构造必须显示定义
public Person(String name){ // 方法重载带参数=>有参构造
this.name = name;
}
// 快捷键生成构造方法:Alt+insert
public Person(String name, int age) {
this.name = name;
this.age = age;
}
}
/*
public static void main(String[] args) {
// 一开始Person中我什么都没写,但是这里在还是可以new出来一个方法
// 说明 在Person类中有一个默认的 public Person(){} 方法 一个空的构造
// new 实例化了一个对象
// 通过debug发现 构造器走完之后new Person();才生成对象
Person person = new Person("大哥",21);
System.out.println(person.name+person.age);
}
小结:
构造器:
1、和类名相同
2、没有返回值
作用:
1、new 本质在调用构造方法
2、初始化对象的值
注意点:
1、定义有参构造之后如果想使用无参构造,那就必须要显示定义一个无参构造
快捷键
Alt+insert
this.属性 = 值 这个值一般是属性传进来的值
*/
对象的创建
package com.oop.demo03;
public class Pets {
public String name;
public int age;
// 无参构造 一个,默认不显示的无参构造方法
public void shout(){
System.out.println("呀呼呀呼的叫");
}
}
/**
*程序从入口main方法开始执行
* 先从new 一个Pest对象开始,会去到Pets这个类里面调用类里面的无参构造方法(默认是不显示的)
* 调用之后通过命名的dog对象 对Pets这个抽象类模板里面的参数进行赋值
* 然后我们的dog对象就有了自己的名字,年龄 和方法
* 注意:调用构造器执行完构造方法之后 new Pets对象才被创建,且对象名叫dog and cat
*/
/*
public static void main(String[] args) {
Pets dog = new Pets();
dog.name = "麻球";
dog.age = 3;
System.out.println("我有一个伙计他叫"+dog.name+"他今年"+dog.age+"岁了");
dog.shout();
Pets cat = new Pets();
cat.name = "喵大哥";
cat.age = 2;
cat.shout();
}
*/
对象创建分析内存图解
对象的创建分析小结:
什么是对象,要明白什么是对象就要先明白面向对象编程的本质!以类的形式组织代码,以对象的形式封装数据!
通过模板(类) 可以创建很多个对象 创建的对象存在内存的堆中,栈中的引用变量名通过堆中不同的地址(0X0001、0X0002)来找到所创建的对象,然后通过引用变量名.属性 的方式 可以给对象中的属性进行赋值。
在创建对象前也就是new pets()时,会先到这个pets类中找到并执行构造方法(个人理解为引用相应的模板) ,如果没写构造方法那么他会调用默认的无参构造方法,当构造方法执行完之后这个对象才被创建, 并赋予一个引用变量名 ,最后通=>过引用变量名.对象的属性 = "值" 的方式来给创建的对象进行赋值(数据封装)
小结一下类与对象
1、类与对象
类是一个模板:抽象,对象是一个具体的实例
2、方法
定义、调用
3、对应的引用
引用类型:基本类型(8) 除了基本类型之外都是引用类型
对象是通过引用来操作的:栈----->堆(地址) 通过栈里面的“引用对象名”来操作对应的对象
4、属性:字段field 或 成员变量
默认初始化:
数字:整数:0 小数浮点数:0.0
char:u0000
boolean:false
引用类型:null
属性的定义:
修饰符 属性类型 属性名 = 属性值!
例:public String name = dog; 公有属性
private String age = 3;私有属性
5、对象的创建和使用
-
必须使用new关键字创造对象,执行完构造器(构造方法) 对象才创建成功 Person person = new Person()
-
对象的属性 person.name
-
对象的方法 person.hello()
6、类里面只能写:
静态的属性 属性
动态的方法 方法
4、面向对象三大特性(重点)
封装
什么是封装?说的简单直白一点就是该露的的露,该藏得藏就好比电视机里面复杂得东西已经给你“封装”好了你只需要摁开关摁扭 换台键
我们得程序设计要追求“高内聚,低耦合”。高内聚:就是类得内部数据操作细节自己完成,不允许外部干涉;低耦合:仅暴露少量得方法给外部使用
封装(数据的隐藏)
{实现封装的注意点:只要重写了类的有参构造,那么无参构造也必须定义出来显示}
通常,应禁止直接访问一个对象中数据的实际表示,而应通过操作接口来访问,这称为信息隐藏
对于代码而言,属性私有:get/set
Alt+insert一键生成get/set 方法
可以在这些set get 方法中对属性进行一些判断可以用于保证数据的合理性
代码如下
Student类
package com.oop.demo04;
// 类 封装的核心 private:私有
public class Stuent {
// 封装的话他的重点相对而言是属性,对属性的封装
// 属性私有
private String name;// 名字
private int id;// 学号
private char sex;// 性别
private int age; //年龄
// 未显示的无参构造方法,默认方法 public Student(){}
// 提供一些可以操作这些属性的方法!
// 提供一些public 的get、set方法 一键生成Alt+insert
// get 获得这个数据
public String getName() {
return this.name;
}
// set 给这个数据设置
public void setName(String name){
this.name =name;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public char getSex() {
return sex;
}
public void setSex(char sex) {
this.sex = sex;
}
public int getAge() {
return age;
}
public void setAge(int age) { // 对设置的值做一些简单的判断
if (age<0 || age>120){ //判断如果年龄不合法
this.age = 3;
}else {
this.age = age;
}
}
}
程序启动类
package com;
import com.oop.demo04.Stuent;
public class Application {
/**
*封装的意义
* 1、提高程序的安全性,保护数据
* 2、隐藏代码的实现细节
* 3、统一接口,统一调用get/set方法 set 方法里面可以做一些安全性的判断
* 4、提高系统的可维护性
*
*/
public static void main(String[] args) {
Stuent stuent = new Stuent();
stuent.setName("擦大哥");
System.out.println(stuent.getName());
// 运行发现我们并没有直接操作封装后的属性,而是通过他提供set get方法来操作属性
// 通过set来设置属性的值,通过get来获取属性设置后的值
// 给student的年龄设置不合法的值
stuent.setAge(200);//正常年龄怎么会超过两百岁?,我们在Student类中对设置属性的值进行了简单的判断,满足条件则输出,不满足则输出3
System.out.println(stuent.getAge()); // 3
}
}
继承
什么是继承?extends 子类继承父类,就会拥有父类得全部方法,但是父类私有得方法或属性是无法直接继承的,子类继承父类继承的是父类的公共属性和方法,一般都是继承父类的属性,如果属性是私有的,除非父类给了相应的get / set 方法来进行调用,否则子类无法继承父类的私有属性。
注意点:Java中只有单继承,没有多继承
- 继承的本质是对某一批类的抽象,从而实现对现实世界更好的建模
- 继承的关键字extends----意思是“扩展”,子类是父类的扩展
关于继承
1、继承是类和类之间的一种关系,除了继承之外,类和类之间的关系还有,依赖,组合,聚合等
2、继承关系的两个类,在一个为子类(派生类),一个为基类(父类)。
3、子类和父类之间,从意义上讲应该具有"is a"的关系
代码效果:
Person类
package com.oop.demo05;
// 在java中,所有的类,都默认之间或间接的继承Object 就好比类里面默认的无参构造方法
// 定义一个父类 人 实现继承的操作
public class Person /*extends Object*/{
// 修饰符的优先级
// public 公共
// protected 受保护
// default 默认
// private 私有的
public int money = 10_0000_0000; // 子类可以直接继承属性
private int money2 = 10_0000_0000; // 不能直接继承需要调用get/set方法
public int getMoney2() {
return money2;
}
public void setMoney2(int money2) {
this.money2 = money2;
}
public void say(){
System.out.println("大威天龙");
}
}
Student类
package com.oop.demo05;
// 学生 is 人 : 派生类:子类 继承父类 Person
// 继承了父类就会拥有父类的全部方法
public class Student extends Person{
// Ctrl + H 可以查看继承树
}
主程序
package com;
import com.oop.demo05.Student;
public class Application {
public static void main(String[] args) {
Student student = new Student();
System.out.println(student.getMoney2()); // 私有的属性
System.out.println(student.money); // 公共的属性
student.say();
// 运行结果
// 1000000000
// 1000000000
// 大威天龙
}
}
super 和 this
Super注意点:
- super调用父类的构造方法,必须在构造方法的第一个
- super 必须只能出现在子类的方法或者构造方法中!
- super 和 this 不能同时调用构造方法
对比 this:
代表的对象不同:
this:本身调用者这个对象
super:代表父类对象的应用
前提
this:没有继承也可以使用
super: 只能在继承条件下才可以使用
构造方法
this(); 当前类的构造!
super(); 父类的构造!
代码实操:
Person类
package com.oop.demo06;
public class Person {
public Person(){// 无参构造
System.out.println("父类无参构造执行了");
}
protected String name = "大威天龙";
// private私有的方法无法被继承
public void print(){
System.out.println("Person");
}
}
Student类
package com.oop.demo06;
public class Student extends Person{ // 继承父类Person
public Student(){
// 隐藏代码:调用了父类的无参构造
super(); // 调用父类的构造器,必须在子类构造器的第一行 this也是
System.out.println("子类的无参构造执行了");
}
private String name = "胸口碎大石";
public void print(){
System.out.println("Student");
}
public void test1(){
print(); // 上面的print()方法
this.print(); // 当前类的print()方法
super.print(); // 父类的print()方法
}
public void test(String name){
System.out.println(name); // 输出的是方法调用时传入的参数
System.out.println(this.name); // 输出的是当前类里面的参数
System.out.println(super.name); // super 子类调用父类的属性
}
}
主程序
package com;
import com.oop.demo06.Student;
public class Application {
public static void main(String[] args) {
Student student = new Student();
// student.test("降龙十八掌");
// student.test1();
}
}
方法重写
根据代码进行理解
A类
package com.oop.demo06;
// 方法重写演示
public class A extends B{
// @Override 重写
@Override // 注解:有功能的注解
public void test() {
System.out.println("A=>test()");
}
}
B类
package com.oop.demo06;
// 重写演示 重写都是方法的重写,和属性无关
public class B {
public /*static*/ void test(){
System.out.println("B=>test()");
}
}
程序入口
package com;
import com.oop.demo06.A;
import com.oop.demo06.B;
public class Application {
// 静态方法和非静态的方法区别很大!
// static静态方法: // 方法的调用只和左边定义的数据类型有关
// 非静态:重写(子父类关系之间才有方法重写)
// 方法的重写只和非静态方法有关与静态方法没有任何关系 方法重写的属性只能是public 不能是私有的属性
public static void main(String[] args) {
// 方法的调用只和左边定义的数据类型有关
A a = new A();
a.test(); // A=>test()
// 父类的引用指向子类 A类是继承 B类的 他两是父子关系
B b = new A(); // 子类重写了父类的方法
b.test(); // B=>test()
}
}
方法重写小结:
重写的前提:有继承关系,子类重写父类的方法!
- 方法名必须相同
- 参数列表必须相同
- 修饰符:范围可以扩大 但是不能缩小 Public>Protected>Default>Private
- 抛出的异常:范围,可以被缩小但是不能被扩大 ClassNotFoundException ---> Exception
重写,子类的方法和父类的方法要一致:方法体不同!
为什么要重写?
- 父类的功能,子类不一定需要,或者不一定满足的时候需要进行方法重写
- 快捷键:Alt + Insert : override;
多态
- 动态编译:类型:可扩展性
- 即同一方法可以根据发送对象的不同而采用多种不同的行为方式?(未理解)
- 一个对象的实际类型是确定的,可以指向对象的引用的类型很多?(未理解)
多态存在的条件
- 有继承关系
- 子类重写父类的方法
- 父类引用指向子类对象
- 注意:多态是方法的多态,属性没有多态性
- instanceof
多态的注意事项
- 多态是方法的多态,属性没有多态
- 父类和子类有联系 如果两个类型没有任何关系进行强转的话 会发生类型转换异常!ClassCastException!
- 多态存在的条件:继承关系,方法需要重写,父类的引用指向子类对象!
如果方法是
- static 方法,属于类,它不属于实例
- final 常量:存在常量池中
- private私有方法
都不能重写,无法重写就实现不了多态
代码实例
Person 类
package com.oop.demo07;
public class Person {
public void run(){
System.out.println("run");
}
}
/**
1. 多态是方法的多态,属性没有多态
2. 父类和子类有联系 如果两个类型没有任何关系进行强转的话 会发生类型转换异常!ClassCastException!
3. 多态存在的条件:继承关系,方法需要重写,父类的引用指向子类对象!
*/
Student 类
package com.oop.demo07;
public class Student extends Person{
@Override
public void run() {
System.out.println("son");
}
public void eat(){
System.out.println("eat");
}
}
程序入口
package com;
import com.oop.demo06.A;
import com.oop.demo06.B;
import com.oop.demo07.Person;
import com.oop.demo07.Student;
public class Application {
public static void main(String[] args) {
// 一个对象的实际类型是确定的
// new Student();
// new Person();
// 可以指向的引用类型就不确定了:父类的引用指向子类
// 结论 一个对象的实际类型是确定的 new Student() 但是可以指向这个对象的引用类型 可以是他任意的父类形包括Obejct 类的祖宗
// 子类和父类有相同的方法如果子类没有重写父类的方法那么调用的是父类的,如果子类重写了父类的方法那么调用的就是子类的
// Student 能调用的方法都是自己的或者继承父类的!
Student s1 = new Student();
// Person 父类型,可以指向子类,但是不能调用子类独有的方法
Person s2 = new Student();
Object s3 = new Student();
s1.run(); // 子类重写了父类的方法,执行之类的方法
s2.run();
// 能执行哪些方法,主要看对象左边的类型,和右边的关系不大
s1.eat();
// s2.eat(); 方法调用失败
}
}
5、抽象类和接口
抽象类
关键字 abstract 用来修饰类或者方法 就是抽象类 和抽象方法
抽象方法只有方法的名字,没有方法的实现
特点:
- 抽象类的所有方法,继承了抽象类的子类,都必须要是实现他的方法 除非子类也是 abstract 那就由他的子子类去重写!
- 不能new 这个抽象类,只能靠子类去实现它:约束
- 抽象类中可以写普通方法,抽象方法必须在抽象类中
- 抽象的抽象:约束
注意点:
抽象类中可以写普通方法,但是抽象方法只能出现在抽象类中
思考?
1、抽象类不能new对象那它存不存在构造器?
结论:实验发现抽象类虽然不能new对象 但是它存在构造器 代码执行前都要经过构造器?
2、抽象类存在的意义是什么?抽象出公共部分 提高开发效率
代码实例
Action类
package com.oop.demo09;
// abstrcat 抽象类:本质是一个类 extends :单继承 java中没有多继承 但是接口可以多继承
public abstract class Action {
// 约束~有人帮我们实现
// abstract 抽象方法 只有方法的名字 没有方法的实现
public abstract void soSomething();
//1. 不能new 这个抽象类,只能靠子类去实现它:约束
//2. 抽象类中可以写普通方法,抽象方法必须在抽象类中
//3. 抽象的抽象:约束
}
子类A
package com.oop.demo09;
//抽象类的所有方法,继承了抽象类的子类,都必须要是实现他的方法 除非子类也是 abstract 那就由他的子子类去重写
public class A extends Action {
@Override
public void soSomething() {
}
}
接口
1、普通类:只有具体实现
2、抽象类:具体实现和规范都有
3、接口:只有规范!
4、接口就是规范,定义的是一组规则,体现了现实世界中”如果你是....则必须能....“的思想。如果你是天使,则必须能飞。如果你是汽车,必须能跑,如果你是奥特曼,则必须打怪兽
5、接口的本质是契约,就像我们的法律一样。制定好后大家都遵守
6、OO的精髓,是对对象的抽象,最能体现这一点的就是接口,为什么我们讨论设计模式都只针对具备了抽象能力的语言(比如C++、Java、C#等),就是因为设计模式所研究的,实际上就是如何合理的去抽象
接口的作用:
- 约束
- 定义一些方法让不同的人实现
- public abstract 抽象方法修饰
- public static final 静态常量
- 接口和抽象类是一样的不能被实例化,并且接口不算类他没有构造方法
- 可以通过implements实现多个接口,实现接口的类必须重写接口的方法
声明类的关键字是class,声明接口的关键字是interface
代码实例
Userservice接口
package com.oop.demo10;
// 抽象思维 ---Java
public interface UserService {
// interface 定义接口的关键字 接口都需要有实现类
// 接口中的所有定义其实都是抽象的 public abstract
// 可以不用写public abstract 接口中是默认的
public abstract void run();
// 可以直接写
void say();
// 定义增删改查方法
void add(String name);
void delete(String name);
void update(String name);
void query(String name);
}
TimeService 接口
package com.oop.demo10;
public interface TimeService {
void timer();
}
UserServiceImpl 接口的实现类 实现多个接口 伪多继承java只有单继承但是接口的实现类可以实现多个接口
package com.oop.demo10;
// 类可以实现接口 implements
// 实现接口的类 必须要重写接口的方法
// 侧面实现多继承
public class UserServiceImpl implements UserService,TimeService {
@Override
public void run() {
}
@Override
public void say() {
}
@Override
public void add(String name) {
}
@Override
public void delete(String name) {
}
@Override
public void update(String name) {
}
@Override
public void query(String name) {
}
@Override
public void timer() {
}
}
6、内部类
内部类
定义:内部类就是一个类的内部再定义一个类,比如A类中定义一个B类,那么B类相对A类来说就称之为内部类,而A类相对相对于B类来说就是外部类
- 成员内部类
- 静态内部类
- 局部内部类
- 匿名内部类
代码实例演示:
Outer类:
package com.oop.demo11;
import javax.crypto.spec.PSource;
/**
* 1. 成员内部类
* 2. 静态内部类
* 3. 局部内部类
* 4. 匿名内部类
*/
public class Outer {
private int id = 50;
public void out(){
System.out.println("这是外部类的方法");
}
// 静态外部类
private static int id2 = 60;
public static void out2(){
System.out.println("static修饰的外部类");
}
// 成员内部类
public class Inner{
public void in(){
System.out.println("这是一个内部类的方法");
}
// 内部类获得外部类的私有属性,和方法
public void getID(){
System.out.println(id); // 获取外部内的属性
Outer.this.out(); // 获取外部内的方法(非静态)
}
}
// 静态内部类
public static class Inner2{
public void in(){
System.out.println("这是一个静态内部内的方法");
}
public void getId(){
System.out.println(id2);// 获取属性
Outer.out2();// 获取方法
}
}
// 局部内部类 //方法中写class
public void method(){
class Inner3{
public void in(){
System.out.println("局部内部类");
System.out.println(id);//调用外部内的属性(非静态)
Outer.this.out(); //调用外部内的方法(非静态)
System.out.println(id2);//静态属性
Outer.out2();// 静态方法
}
}
}
// public static void main(String[] args) {
// new Apple().eat();
// new UserService(){
// @Override
// public void hello() {
// System.out.println("匿名内部类和你说hello");
// }
// };
// }
}
// 匿名内部类 写在外部类之外 没有名字初始化类,不用将实例保存到变量中
// 通过匿名内部类还可以new 接口的类 一般接口是不能new 的但是通过匿名内部类可以实现
class Apple{
public void eat(){
System.out.println("匿名内部类");
}
}
interface UserService{
void hello();
}
主程序:
package com;
import com.oop.demo11.Outer;
public class Application {
public static void main(String[] args) {
System.out.println("成员内部类");
Outer outer = new Outer();
// outer.getClass();这一段是隐藏代码
// 奇葩方式,通过这个外部类来实例化内部类
Outer.Inner inner = outer.new Inner();
inner.in();
inner.getID();
System.out.println("==========================");
System.out.println("静态内部类");
Outer.Inner2 inner2 = outer.new Inner2();
inner2.in();
inner2.getId();
System.out.println("===========================");
System.out.println("局部内部类");
System.out.println("只能在当前class中使用无法直接调用");
System.out.println("=============================");
System.out.println("匿名内部类");
System.out.println("匿名内部类也是无法new 来进行调用的 匿名内部类 写在外部类之外 没有名字初始化类,不用将实例保存到变量中");
}
}
异常
- 什么是异常
- 异常体系结构
- java异常处理机制
- 处理异常
- 自定义异常
- 总结
错误ERROR:错误不是异常,而是脱离程序员控制的问题,错误在代码中通常被忽略,例如当栈溢出时。一个错误就发生了,他们在编译时检查不到
错误无法预料的,异常是可以预料的
什么是异常
实际工作中遇到的情况不可能是非常完美的,比如:你写的某个模块,用户输入不符合你的要求,你的程序要打开某个文件,这个文件不存在或则文件格式不对,你要读取的数据,数据可能是空的等!或者我们的程序在跑着,内存或硬盘可能满了.....
我们把这一类异常问题叫异常,Exception(例外)
异常指的是程序中不期而至的各种状况,如:文件找不到,网络连接失败,非法参数等
异常发生在程序运行期间,他影响了正常的程序执行流程。
异常可以大致分为两类
运行时异常
RuntimeException(运行时异常)是Exception分支中一个重要的子类
- ArrayIndexOutOfBoundsException(数组下标越界异常)
- NullPointerException(空指针异常)
- 什么是空指针异常?Java中不是没有指针嘛?其实控股指针异常就是java中对象的引用。比如String s = null;这个s就是指针。
- 所谓的空指针,就是指针的内容为空,比如上面的s,如果令它指向null,就是空指针。
- 所谓的空指针异常,就是一个指针是空指针,你还要去操作它,既然它指向的是空对象,它就不能使用这个对象的方法。比如上面的s假如为null,你还要用s的方法,比如s.equals( String x);那么就会产生空指针异常
- ArithmeticException(算术异常)
- MissingResourceException(丢失资源)
- ClassNotFoundException(找不到类)
- 等等异常
这些异常不是检查异常,程序中可以选择捕获处理,也可以不处理
这些异常一般是由程序逻辑错误引起的,程序应该从逻辑角度尽可能的避免这类异常的发生
非运行时异常
也就是检查异常
ERROR
Error类对象由Java虚拟机生成并抛出,大多数错误与代码编写者执行的操作无关
Java虚拟机运行误(Virtual MachineError),当JVM不再有继续执行操作所需的内存资源时,将出现OutOfMemoryError(内存溢出) 这些异常发生时,Java虚拟机(JVM)一般会选择线程终止
还有发生在虚拟机试图执行应用时,如类定义的错误(NoclassDeFoundError)、链接错误(LinKageError).这些错误是不可查的,因为它们在应用程序的控制和处理能力之外,而且绝大多数程序运行时不允许出现的状况
小结ERROR和异常的区别
ERROR通常是灾难性的致命的错误,是程序无法控制和处理的,当出现这些异常时,Java虚拟机(JVM)一般会选择终止线程;Exception通常情况下时可以被程序处理的,并且程序中应该尽可能的去处理这些异常
异常的处理(异常捕获抛出)
抛出异常
捕获异常
异常处理的五个关键字
- try、catch、finally、throw、throws
try:尝试着去处理某个事物
catch:捕获
finally:无论执不执行最后都会走这个地方
throw、throws:抛出异常
// try catch一般是一起使用的 finally可以写可以不写
// 用来处理善后工作 比如IO流、资源关闭等等
try{
}catch(excpection e){
}finally{
}
catch(异常类型 e){对异常进行处理}
catch 可以实现捕获多个异常 使用多个catch 捕获,但是要遵循从上到下的异常级别是从小到达
throwable > (Exception&&Error)
throw主动抛出异常,一般用在方法内 就比如一个除法在运算前我们就知道被除数不能为0 那么如何避免用户在被除数为0后程序终止 可以在方法执行之前 主动抛出被除数为0 的异常。保证就算被除数输入的是0也可以继续执行!
throws也是主动抛出异常但是它是用在方法之上,一般是方法中的异常无法处理的时候,可以在方法之上对异常进行抛出!然后调用方法时通过 try{}catch(异常类型){} 的形式捕获异常保证程序的执行!
try{}catch(){}finally{} 快捷键生成: ctrl + Alt +t
代码示例
demo02类
package com.execption;
import com.oop.demo09.Test;
public class demo02 {
public static void main(String[] args) {
// 异常捕获 try catch finally
int a = 1;
int b = 0;
try { //监控区域尝试运行代码
System.out.println(a/b);
}catch (ArithmeticException e){ //catch 捕获区域 对异常进行捕获
System.out.println("程序异常了,b能等于0");
}finally { //处理善后工作
System.out.println("finally");
}
try {
new demo02().a();
}catch (Throwable e){//异常类型 Throwable级别最大的
System.out.println("异常捕获成功"+e); // StackOverflowError栈溢出
}finally {
System.out.println("资源关闭");
}
// try carch 中的catch可以多连续使用 捕获多个异常 但是catch(异常类型)中的异常类型级别重上到下,要保证级别从小到大
try{
new demo02().b();
// System.out.println(a/0);
}catch (Error error){
System.out.println(error);
System.out.println("Error异常");
}catch (Exception exception){
System.out.println(exception);
System.out.println("Exception异常");
}catch (Throwable throwable){
System.out.println(throwable);
}finally {
System.out.println("异常捕获完毕");
}
// 选中代码快 ctrl + alt +t 可以选择生成try catch finally 方法
try {
System.out.println(a/0);
} catch (Exception e) {
e.printStackTrace(); // 打印错误的栈信息
} finally {
System.out.println("ctrl + Alt + t");
}
// throw 主动抛出异常一般在方法中使用 throws在方法上抛出异常 用于方法中处理不了的异常
try {
new demo02().test(1,0);
} catch (ArithmeticException e) {
e.printStackTrace();
}
}
// 假设这个方法中处理不了这个异常 还可以通过throws在方法上异常
public void test(int a,int b) throws ArithmeticException{ // throws 方法中无法处理的异常可以在方法上进行处理
if (b==0){
throw new ArithmeticException();// 主动抛出可以预见的异常 一般用在方法中
}
}
// 模拟循环调用出错 进行捕获抛出
public void a(){b();}
public void b(){a();}
}
自定义异常
用户自定义异常,只需要继承Exception类即可
大致步骤:
- 创建自定义异常类
- 在方法中通过throw关键字抛出异常对象
- 如果在当前抛出异常得方法中处理异常,可以使用try-catch语句捕获并处理;否则在方法得声明处通过throws关键字指明要抛出给方法调用者得异常继续进行下一步操作
- 在出现异常得方法调用者中捕获并处理异常
代码案例
MyException类
package com.execption;
public class MyException extends Exception {
// 传递数字>10;
private int detail;
public MyException(int a ) {
this.detail = a;
}
// toString异常打印信息
@Override
public String toString() {
return "MyException{" + detail + '}';
}
}
Test测试类
package com.execption;
public class Test {
// 可能会存在异常的方法
static void test(int a) throws MyException { // 方法上抛出
System.out.println("传递的参数为"+a);
if (a>10){
throw new MyException(a);
}else {
System.out.println("OK");
}
}
static void test2(int b){ // 方法内抛出
System.out.println("test2传递的参数为"+b);
if (b>10){
try {
throw new MyException(b); // 通过alt+enter补全来选择是方法内捕获还是方法外捕获
} catch (MyException e) {
System.out.println("test MyException =>"+e);
}
}
}
public static void main(String[] args){ // 调用test方法方法上抛出异常在调用时对异常继续捕获
try {
new Test().test(11);
} catch (MyException e) {
System.out.println("test2 MyException =>"+e);
}
}
// public static void main(String[] args) { // 调用test2方法中抛出异常并且处理
// new Test().test2(11);
// }
}