面向对象编程
一、初识面向对象
二、方法回顾与加深
break和return的区别
break:跳出switch,结束循环;
return:结束方法,返回内容(与返回类型进行对应,viod可以返回空)。
public class Demo02 {
public static void main(String[] args) {
//静态方法 调用
Student.say();
//非静态方法(需要实例化这个对象new)
Student student = new Student();
student.play();
}
// static时和类一起加载的,但是不加static是与对象有关的,需要类实例化之后才存在
// 两个方法同为static,或者同无static才可以互相调用,否则不可以互相调用
public void a(){
b();
}
public void b(){
a();
}
public static void c(){
d();
}
public static void d(){
c();
}
}
//值传递
public class Demo04 {
public static void main(String[] args) {
int a=1;
System.out.println(a);//1
change(a);
System.out.println(a);//1,这就是值传递,change方法改变的值只是形参,并没有返回,所以main方法中的a,不会改变
}
public static void change(int a){
a=10;
}
}
//引用传递:传的是对象,本质还是值传递
public class Demo05 {
public static void main(String[] args) {
Person person = new Person();
System.out.println(person.name);//null
change(person);
System.out.println(person.name);//荔枝,这就是引用传递
}
public static void change(Person person){
person.name="荔枝";//这里修改的是person类里面的name值,person是一个对象,指向:Person person = new Person();
}
}
//定义了一个Persong类,有一个属性:name
class Person{
String name;
}
三、对象的创建分析
public class Student {
String name;//null
int age;//0
public void study(){
System.out.println(this.name+"在学习");
}
}
public class Application {
public static void main(String[] args) {
//类:抽象的,需要实例化
//类实例化后会返回一个自己的对象
//student对象就是一个Student类的具体实例
Student xiaoMing = new Student();
Student xiaoHong = new Student();
xiaoMing.name="小明";
xiaoMing.age=3;
System.out.println( xiaoMing.name);
System.out.println( xiaoMing.age);
xiaoHong.name="小红";
xiaoHong.age=5;
System.out.println( xiaoHong.name);
System.out.println( xiaoHong.age);
}
}
public class Person {
//一个类什么都不写,它也会存在一个方法
//显示定义构造器
String name;
//实例化初始值
//1.使用new关键字,本质是在调用构造器
//2.构造器用来初始化值
public Person(){//无参构造
}
// 一旦定义了有参构造,想通过无参构造new对象,那么无参构造就必须显示定义
public Person(String name){//有参构造
this.name=name;
}
//快捷键 alt+insert 选择第一个constructor创建构造器
/**
* public static void main(String[] args) {
* Person person = new Person("荔枝");
* System.out.println(person.name);
* }
*/
}
构造器:
1.和类名相同
2.没有返回值
作用:
1.new 本质在调用构造方法
2.初始化对象的值
注意点:
1.定义有参构造之后,如果想使用无参构造,显示的定义一个无参的构造
Alt + Insert 快捷键
package com.oop.demo03;
public class Pet {
public String name;
public int age;
public void shout(){
System.out.println("叫了一声");
}
/*
public static void main(String[] args) {
Pet dog = new Pet();
dog.name="旺财";
dog.age=3;
dog.shout();
System.out.println(dog.name);
System.out.println(dog.age);
Pet cat = new Pet();
}
*/
}
对应的内存图
小结:
- 类与对象
类是一个模板:抽象,对象是一个具体的实例 - 方法——定义、调用!
- 对应的引用
引用类型 :基本类型(8)
对象是通过引用来操作的:栈--->堆 - 属性:字段Field 成员变量
默认初始化: 数字:0 0.0 char :u0000 boolean:false 引用:null
定义:修饰符 属性类型 属性名=属性值! - 对象的创建和使用
必须使用new 关键字创造对象,构造器 Person kuangshen= new Person();
对象的属性kuangshen.name
对象的方法kuangshen.sleep() - 类(静态的属性——属性、动态的行为——方法)
四、面向对象三大特性★★★★★
封装
//封装
/**
* 1.提高程序的安全性,保护数据
* 2.隐藏代码的实现细节
* 3.统一接口
* 4.系统的可维护性增加
*/
public class Student {
// 属性私有
private String name;//姓名
private int id;//学号
private char sex;//性别
private int age;
//提供一些可以操作这个属性的方法
// 提供一些public的get、set方法
//get获取值
public String getName() {
return name;
}
//set设置值
public void setName(String name) {
this.name = name;
}
public char getSex() {
return sex;
}
public void setSex(char sex) {
this.sex = sex;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public int getAge() {
return age;
}
public void setAge(int age) {
if(age>0&&age<120){
this.age = age;
}else {//数据不合法
this.age = 3;
}
}
}
继承
//所有的类都默认直接或者间接继承object类
public class Person /*extends Object*/{//父类、基类
//修饰符:public protected default private
private int money=10_0000_0000;
public void say(){
System.out.println("说了一句话");
}
public int getMoney() {
return money;
}
public void setMoney(int money) {
this.money = money;
}
}
// student is a person
public class Student extends Person{//子类、派生类
//快捷键 ctrl+H 可以看到继承关系
//一个儿子只能有一个爸爸,一个爸爸可以有多个儿子
}
// teacher is a person
public class Teacher extends Person{//子类、派生类
}
public static void main(String[] args) {
Student student = new Student();
student.say();//虽然student类中没有say方法以及mongey属性,但是仍然可以调用,就是因为继承了父类的public
System.out.println(student.getMoney());
}
super
public class Person {
protected String name="荔枝";
public Person() {
System.out.println("Person无参构造执行了");
}
public void print(){//如果修饰符换成private则无法继承,私有的不能继承
System.out.println("Person");
}
}
public class Student extends Person{
private String name="ly";
public Student() {
//这里有一句隐藏代码 super(); 执行父类的无参构造
super();// 必须在子类构造器的第一行,this()的使用也是这样
System.out.println("Student无参构造执行了");
}
public void print(){
System.out.println("Student");
}
public void test1(){
print();
this.print();
super.print();
}
public void test(String name){
System.out.println(name);//李颖
System.out.println(this.name);//ly
System.out.println(super.name);//荔枝
}
}
public static void main(String[] args) {
Student student = new Student();//先调用了父类的无参构造,在调用的子类的无参构造
// student.test("李颖");// 李颖 ly 荔枝
// student.test1();//Student Student Person
}
super注意点:
1.super调用父类的构造方法,必须在构造方法的第一个
2.super 必须只能出现在子类的方法或者构造方法中!
3.super和 this 不能同时调用构造方法!
Vs this:
代表的对象不同:
this:本身调用者这个对象
super:代表父类对象的引用
前提不同
this:没有继承也可以使用
super:只能在继承条件才可以使用
构造方法不同
this():本类的构造
super():父类的构造
方法重写
public class B {
public static void test(){
System.out.println("B=>test()");
}
}
public class A extends B{
public static void test(){
System.out.println("A=>test()");
}
}
//静态方法与非静态方法的区别很大
//静态方法:方法的调用只和左边,即定义的数据类型有关
//非静态方法:重写
public static void main(String[] args) {
A a = new A();
a.test();//A=>test()
//父类的引用指向了子类
B b = new A();
b.test();//当两个类中的方法都为static时输出:B=>test(),当不是static而是进行了重写时输出:B=>test()
}
重写:需要有继承关系的前提,子类重写父类的方法!
1.方法名必须相同
2.参数列表列表必须相同
3.修饰符:范围可以扩大但不能缩小:public>Protected>Default>private
4.抛出的异常:范围,可以被缩小,但不能扩大;classNotFoundException -->Exception(大)
重写,子类的方法名和参数和父类必须一致;方法体不同!
为什么需要重写?:父类的功能,子类不一定需要,或者不一定满足!快捷键:Alt + Insert :override
多态
动态编译类型,增强可扩展性
public class Person {
public void run(){
System.out.println("run");
}
}
public class Student extends Person{
public void run(){
System.out.println("son");
}
public void eat(){
System.out.println("eat");
}
}
public static void main(String[] args) {
// 一个对象的实际类型是确定的
// new Student();
// new Person();
//可以指向的引用类型就不确定,可以是父类的引用指向子类
Student student = new Student();//Student 能调用的方法都是自己的或者继承父类的
Person student1 = new Student();//Person 父类型,可以指向子类,但是不能调用子类独有的方法
Object student2 = new Student();
//对象执行了哪些方法,主要是与左边的类型有关,只有子类进行重写后,即使左边的父类,也会执行子类的方法
student.run();
student1.run();// 子类重写了父类的方法,执行子类的方法
student.eat();
}
多态注意事项:
1.多态是方法的多态,属性没有多态
2.需要存在父类和子类的联系 ,否则会出现类型转换异常:ClassCastException
3.存在条件:继承关系,方法需要重写,父类引用指向子类对象:Father f1 = new Son();
哪些不能进行重写?:1.static 方法,属于类,它不属于实例;2.final 常量 3.private方法
instanceOf关键字
用于判断两个类是否存在父子关系,true/false
public static void main(String[] args) {
//Object>String
//Object>Person>Student
//Object>Person>Teacher
// System.out.println(X instanceof Y); 能不能编译通过主要是看X和Y是否具有父子关系
Object object = new Student();
System.out.println(object instanceof Student);//true
System.out.println(object instanceof Person);//true
System.out.println(object instanceof Object);//true
System.out.println(object instanceof Teacher);//false
System.out.println(object instanceof String);//false
System.out.println("================================");
Person person = new Student();
System.out.println(person instanceof Student);//true
System.out.println(person instanceof Person);//true
System.out.println(person instanceof Object);//true
System.out.println(person instanceof Teacher);//false
// System.out.println(person instanceof String);//编译不通过
System.out.println("================================");
Student student = new Student();
System.out.println(student instanceof Student);//true
System.out.println(student instanceof Person);//true
System.out.println(student instanceof Object);//true
// System.out.println(student instanceof Teacher);//编译不通过
// System.out.println(student instanceof String);//编译不通过
System.out.println("================================");
}
类型转换
public static void main(String[] args) {
//类型的转换 :父与子
//高 低
Person obj = new Student();
//无法使用obj.go();调用student的go方法,因为obj是一个person类型的对象
//需要进行强制转换才可以使用,由高到低
//student将这个对象转换为Student类型,就可以使用Student类型的方法了
((Student)obj).go();
Student student = new Student();
student.go();
Person person=student;
// person.go();//编译错误,无法使用;子类转换为父类,可能会丢失自身本来的一些方法
}
1.父类引用指向子类的对象
2.把子类转换为父类,向上转型;自动转换
3.把父类转换为子类,向下转型,需要强制转换;
4.作用:方便方法的调用,减少重复的代码,简洁
static补充
public class Student {
public static int age;// 静态变量 多线程
public double score;// 非静态变量
public void run(){
go();//非静态方法可以使用静态方法
}
public static void go(){
// run();//静态方法不能使用非静态方法,静态方法是和类一起加载的,那时还没有非静态的方法加载
}
public static void main(String[] args) {
go();
new Student().run();
// Student stu = new Student();
// System.out.println(Student.age);
// System.out.println(stu.age);
// System.out.println(stu.score);
}
}
public class Person {
{
//代码块(匿名代码块)
System.out.println("匿名代码块");
}
static {
//静态代码块(加载初始化的数据),永久只执行一次
System.out.println("静态代码块");
}
public Person() {
System.out.println("构造方法");
}
public static void main(String[] args) {
Person person = new Person();//先执行静态代码块,再执行匿名代码块,最后是构造方法
Person person1 = new Person();//先执行匿名代码块,再是构造方法,这是没有静态代码块了,它只执行一次
}
}
import static java.lang.Math.random;
import static java.lang.Math.PI;
public class Test {
public static void main(String[] args) {
System.out.println(random());//经过静态导入包后,就不用使用Math.random()生成随机数了,可以直接使用random
System.out.println(PI);
}
}
五、抽象类与接口
抽象类
//abstract 关键字定义抽象类
public abstract class Action {
//这是一个约束方法,有人帮我们实现,就可以定义为抽象方法
// abstract 关键字定义抽象方法 ,只有方法名字,没有具体的实现
public abstract void doSomething();
}
//抽象类的所有方法,继承了它的子类,都必须实现它的方法,除非继承它的也是一个抽象类
//java的类只能单继承,但是接口可以多继承
public class A extends Action{
@Override
public void doSomething() {
}
}
注意:
1.不能new这个抽象类,只能靠子类去实现它;
2.抽象类中可以写普通的方法
3.抽象方法必须在抽象类中
就是抽象的抽象,一种约束
思考题:
抽象类存在构造器么?——不存在
存在的意义?——抽象出来 提高开发效率
接口
//interface 定义的关键字,接口都需要有实现类
public interface UserService {
//接口中定义的变量都是常量
public static final int AGE=99;
// 接口中的所有定义其实都是抽象的 public abstract 被省略
public abstract void add(String name);
void delete(String name);
void update(String name);
void query(String name);
}
public interface TimeService {
void timer();
}
//implements 关键字:通过类实现接口
// 实现了接口的类,就需要重写接口中的所有方法
//利用接口实现多继承
public class UserServiceImpl implements UserService,TimeService{
@Override
public void timer() {
}
@Override
public void add(String name) {
}
@Override
public void delete(String name) {
}
@Override
public void update(String name) {
}
@Override
public void query(String name) {
}
}
接口作用:
-
是一种约束
-
定义一些方法,让不同的人实现
-
接口中的方法都是public abstract 修饰的
-
接口中的变量都是public static final 修饰的
-
接口不能被实例化,因为接口中没有构造方法(抽象类也不能被实例化,只能继承)
-
通过implements关键字,可以实现多个接口
-
实现类必须要重写接口中的方法
六、内部类
内部类就是在一个类的内部再定义一个类,比如,A类中定义一个B类,那么B类相对于A类来说就是称为内部类,而A类相对B类就是外部类。
成员内部类
public class Outer {
private int id = 10;
public void out(){
System.out.println("这是一个外部类的方法");
}
public class Inner {
public void in(){
System.out.println("这是一个内部类的方法");
}
//内部类可以获得外部类的私有属性或方法!!!
public void getId(){
System.out.println(id);
}
}
}
public class Application {
public static void main(String[] args) {
Outer outer = new Outer();
//通过外部类来实例化内部类
Outer.Inner inner = outer.new Inner();
inner.in();
}
}
静态内部类
成员内部类加上Static关键字就是静态内部类。
补充:
public class Outer {
}
//一个JAVA类中,可以有多个class类,但是只能有一个public class
class A{
public static void main(String[] args) {
}
}
局部内部类
public class Outer {
//局部内部类
public void method(){
class Inner{
public void in(){}
}
}
}
匿名内部类
public class Test {
public static void main(String[] args) {
//没有名字初始化类,不用将实例化保存到变量中
new Apple().eat();
new UserService(){
@Override
public void hello() {
}
};
}
}
class Apple{
public void eat(){
System.out.println("apple");
}
}
interface UserService{
void hello();
}
七、异常机制
异常体系结构
Error:
◆Error类对象由 Java 虚拟机生成并抛出,大多数错误与代码编写者所执行的操作无关
◆java虚拟机运行错误(Virtual MachineError),当JVM不再有继续执行操作所需的内存资源时,将出现 OutOfMemoryError。这些异常发生时,Java虚拟机(JVM)一般会选择线程终止:
◆还有发生在虚拟机试图执行应用时,如类定义错误(NoClassDefFoundError)、链接错误(LinkageError)。这些错误是不可查的,因为它们在应用程序的控制和处理能力之 外,而且绝大多数是程序运行时不允许出现的状况。
Exception:
◆在Exception分支中有一个重要的子类RuntimeException(运行时异常)
◆ ArraylndexOutOfBoundsException(数组下标越界)
◆NullPointerException(空指针异常)
◆ ArithmeticException(算术异常)
◆ MissingResourceException(丟失资源)
◆ClassNotFoundException(找不到类)等异常,这些异常是不检查异常,程序中可以选择捕获处理,也可以不处理。
◆这些异常一般是由程序逻辑错误引起的,程序应该从逻辑角度尽可能避免这类异常的发生
◆Error和Exception的区别:Error通常是灾难性的致命的错误,是程序无法控制和处理的,当出现这些异常时,Java虚拟机(JVM)一般会选择终止线程;Exception通常情况下是可以被程序处理的,并且在程序中应该尽可能的去处理这些异常。
JAVA异常处理机制
异常处理的5个关键字:try、catch、finally、throw、throws
public static void main(String[] args) {
int a=1;
int b=0;
//假设需要捕获多个异常,范围一定是从小到大
try{//try监控区域
System.out.println(a/b);
}catch(ArithmeticException e){ //捕获异常,参数是想要捕获的异常类型
System.out.println("程序出现异常!");
}catch (Exception e){//可以捕获多个异常,从小到大的范围,只会对应输出一个
System.out.println("exception 异常");
}finally { //处理善后工作,可选,可以不要finally。假设在IO流中,可以用于关闭资源
System.out.println("finally");
}
}
public class Test {
public static void main(String[] args) {
try {
new Test().test(1,0);
} catch (ArithmeticException e) {
e.printStackTrace();
} finally {
}
}
//假设这个方法中,处理不了这个异常,那就在方法上抛出异常 throws
public void test(int a,int b) throws ArithmeticException {
if(b==0){
throw new ArithmeticException();//主动的抛出异常,一般在方法中使用
}
System.out.println(a/b);
}
}
//快捷键:选中需要包裹内容, ctrl alt +T
自定义异常
◆使用Java内置的异常类可以描述在编程时出现的大部分异常情况。除此之外,用户还可以自定义异常。用户自定义异常类,只需继承Exception类即可。
◆在程序中使用自定义异常类,大体可分为以下几个步骤
1. 创建自定义异常类。
2. 在方法中通过throw关键字抛出异常对象。
3. 如果在当前抛出异常的方法中处理异常,可以使用try-catch语句捕获并处理;否则在方法的声明处通过throws关键字指明要抛出给方法调用者的异常,继续进行下一步操作。
4. 在出现异常方法的调用者中捕获并处理异常。
//自定义的异常
public class MyException extends Exception{
private int detail;
public MyException( int detail) {
this.detail = detail;
}
//toString 打印异常信息
@Override
public String toString() {
return "MyException{" +
"detail=" + detail +
'}';
}
}
public class Test {
static void test(int a) throws MyException {
System.out.println("传递的参数为:"+a);
if(a>10){
throw new MyException(a);
}
System.out.println("OK");
}
public static void main(String[] args) {
try {
test(20);
} catch (MyException e) {
System.out.println(e);;
}
}
}
经验总结:
◆处理运行时异常时,采用逻辑去合理规避同时辅助 try-catch 处理
◆在多重catch块后面,可以加一个catch(Exception)来处理可能会被遗漏的异常
◆对于不确定的代码,也可以加上 try-catch,处理潜在的异常尽量去处理异常,切忌只是简单地调用 printStackTrace()去打印输出,可以增加一些处理异常的代码
◆具体如何处理异常,要根据不同的业务需求和异常类型去决定
◆尽量添加finally语句块去释放占用的资源(比如IO流)