前言:这个java视频非常适合零基础小白的同学,老师讲的非常细,而且全都是实用方法,没有多余的废话,课时也相对来说不长不短,尤其适合零基础小白!!!由于本人有一定的编码基础,所以一些非常小白的笔记就没有写,如果有需要的,可以在视频评论区免费获取。
文章目录
继承
什么是继承
-
父类【超类/基类】怎么形成的:我们的定义了多个类,发现这些类中有很多重复性的代码,我们就定义了一个父类,将相同的代码抽取出来放到父类中,其他的类直接继承这个父类,就可以直接使用父类中的内容了。
-
怎么去继承: 子类 extends 父类
-
注意:
a. 子类可以继承父类中私有和非私有成员,但是不能使用父类中私有成员
b.构造方法不能继承
继承如何使用
- 定义一个父类,在其中定义重复性的代码
- 定义一个子类继承父类:子类 extends 父类
- 创建子类对象,直接使用父类中非私有成员
java
public class Employee {
String name;
int age;
public void work(){
System.out.println("工作");
}
private void eat(){
System.out.println("吃饭");
}
}
java
public class Teacher extends Employee{
}
java
//TIP 要<b>运行</b>代码,请按 <shortcut actionId="Run"/> 或
// 点击装订区域中的 <icon src="AllIcons.Actions.Execute"/> 图标。
public class Main {
public static void main(String[] args) {
Teacher teacher = new Teacher();
teacher.name = "222";
teacher.age = 22;
teacher.work();
//子类继承父类之后不能使用父类私有成员,只能使用父类非私有成员
//teacher.eat();
}
}
继承中成员的访问特点
- 继承前提下,成员变量访问特点口诀:看等号左边是谁,先调用谁中的成员,子类没有,找父类
- 成员方法:看new的是谁,先调用谁中的方法,子类没有,找父类
子类和父类中的成员变量不重名
看等号左边是谁,先调用谁中的成员
如果等号左边是父类类型,只能调用父类中的成员变量,如果等号左边是子类类型,既能调用子类的,还能调用父类中继承过来的非私有成员
java
package b;
public class Fu {
int numfu = 100;
}
java
package b;
public class Zi extends Fu{
int numzi = 10;
}
java
package b;
public class Test01 {
public static void main(String[] args) {
Fu f = new Fu();
System.out.println(f.numfu);
//System.out.println(f.numzi); 不能直接调用子类特有成员
System.out.println("=============================");
Zi zi = new Zi();
System.out.println(zi.numzi);
System.out.println(zi.numfu); //继承了父类,可以使用父类的非私有成员
}
}
子类和父类中的成员变量重名
看等号左边是谁,先调用谁中的成员
如果等号左边是父类类型,只能调用父类中的成员变量,如果等号左边是子类类型,既能调用子类的,还能调用父类中继承过来的非私有成员
java
package b;
public class Fu {
int numfu = 100;
int num = 1;
}
java
package b;
public class Zi extends Fu{
int numzi = 10;
int num = 2;
}
java
package b;
public class Test01 {
public static void main(String[] args) {
Fu f = new Fu();
System.out.println(f.numfu); //100
System.out.println(f.num); //1
//System.out.println(f.numzi); 不能直接调用子类特有成员
System.out.println("=============================");
Zi zi = new Zi();
System.out.println(zi.numzi); //10
System.out.println(zi.numfu); //100,继承了父类,可以使用父类的非私有成员
System.out.println(zi.num); //2
}
}
成员方法
成员方法:看new的是谁,先调用谁中的方法,子类没有,找父类
java
package c;
public class Fu {
public void methodfu(){
System.out.println("我是父类中的methodFu方法");
}
public void method(){
System.out.println("我是父类中的method方法");
}
}
java
package c;
public class Zi extends Fu {
public void methodzi(){
System.out.println("我是子类中的methodzi方法");
}
public void method(){
System.out.println("我是子类中的method方法");
}
}
java
package c;
public class Test01 {
public static void main(String[] args) {
Fu f = new Fu();
f.methodfu();
//f.methodzi(); 不能直接调用子类特有方法
f.method(); //我是父类中的method方法
System.out.println("========================");
Zi zi = new Zi();
zi.methodfu(); //继承父类之后,能调用父类非私有成员
zi.methodzi();
zi.method(); //我是子类中的method方法
System.out.println("========================");
Fu f1 = new Zi();
f1.method(); //我是子类中的method方法
}
}
方法的重写
- 概述:子类中有一个和父类方法名以及参数列表相同的方法
- 前提:继承
- 访问:看new的是谁,先调用谁中的,如果new的是子类,调用调用子类重写的方法,子类没有,找父类
- 检测是否为重写方法:在该方法上写:@Override
java
package d;
public class Zi extends Fu {
public void methodzi(){
System.out.println("我是子类中的methodzi方法");
}
@Override
//@Override未报错,重写的方法
public void method(){
System.out.println("我是子类中的method方法");
}
}
- 子类重写父类方法之后,权限必须要保证大于等于父类权限 (权限指的是访问权限)
public -> protected -> 默认 -> private - 子类方法重写父类方法,方法名和参数列表要一样
- 私有方法不能被重写,构造方法不能被重写,静态方法不能被重写
- 子类重写父类方法之后,返回值类型应该是父类方法返回值类型的子类类型
java
package d;
public class Fu {
public void methodfu(){
System.out.println("我是父类中的methodfu方法");
}
public void method(){
System.out.println("我是父类中的method方法");
}
public void method1(){}
public static void method2(){}
public Fu method3(){
return null;
}
public Zi method4(){
return null;
}
}
java
package d;
public class Zi extends Fu {
public void methodzi(){
System.out.println("我是子类中的methodzi方法");
}
@Override
//@Override未报错,重写的方法
public void method(){
System.out.println("我是子类中的method方法");
}
//@Override
//void method1(){} 默认权限小于public权限
//@Override 静态方法不能被重写
public static void method2(){}
@Override
public Zi method3(){
return null;
}
//子类重写父类方法之后,返回值类型应该是父类方法返回值类型的子类类型
// public Fu method4(){
// return null;
// }
}
super和this
继承中构造方法的特点
- new子类对象时,会先初始化父类(先走父类无参构造方法)
- 原因:每个构造方法的第一行,默认都会有一个super(),不写jvm自动提供一个,super()代表的是父类无参构造。
java
package e;
public class Fu {
public Fu(){
System.out.println("我是父类中的无参构造");
}
}
java
package e;
public class Zi extends Fu{
public Zi(){
System.out.println("我是子类中的无参构造");
}
public Zi(int a){
System.out.println("我是子类中的有参构造");
}
}
java
package e;
public class Test {
public static void main(String[] args) {
Zi zi = new Zi(); //我是父类中的无参构造 我是子类中的无参构造
System.out.println("========================");
Zi zi1 = new Zi(10); //我是父类中的无参构造 我是子类中的有参构造
}
}
super的具体使用
-
概述:代表的是父类引用
-
作用:可以调用父类中的成员
-
使用:
a.调用父类构造方法-> 在子类中的构造中写
super() -> 调用父类无参构造
super(实参) -> 调用父类有参构造
b.调用父类成员变量:super.成员变量名
c.调用父类成员方法: super.成员方法名(实参)
java
package f;
public class Fu {
int num = 10;
public Fu(){
System.out.println("我是父类的无参构造");
}
public Fu(int data){
System.out.println("我是父类中的有参构造");
}
public void method(){
System.out.println("我是父类中的method方法");
}
}
java
package f;
public class Zi extends Fu {
int num = 100;
public Zi(){
super(); //调用父类中的无参构造
System.out.println("我是子类中的无参构造");
}
public Zi(int num){
super(10);
System.out.println("我是子类中的有参构造");
}
public void method(){
super.method(); //调用父类的method方法
System.out.println("我是子类中的method方法");
System.out.println("num = " + num); //就近原则 子类自己的num
System.out.println("super.num = " + super.num); //父类的num
}
}
java
package f;
public class Test {
public static void main(String[] args) {
Zi zi = new Zi(); //我是父类中的无参构造 我是子类中的无参构造
System.out.println("==========================");
Zi zi1 = new Zi(10); //我是父类中的有参构造 我是子类中的有参构造
System.out.println("==========================");
Zi zi2 = new Zi();
zi1.method(); //我是子类中的method方法
}
}
this的具体使用
-
this概述:代表的是当前对象(哪个对象调用的this所在的方法,this就代表哪个对象)
-
作用:
a.区分重名的成员变量和局部变量
b.调用当前对象中的成员
-
使用:
a.调用当前对象的构造:在构造中写
this():调用当前对象的无参构造
this(实参):调用当前对象的有参构造
b.调用当前对象的成员变量:
this.成员变量名
c.调用当前对象的成员方法:
this.成员方法名(实参)
-
注意:
不管是super还是this,只要在构造中使用,都必须在第一行,所以二者不能同时手写出来
java
package g;
public class Person {
int num = 10;
public Person(){
//this(10); //无参构造调有参构造
System.out.println("我是person类中的无参构造");
}
public Person(int num){
this(); //有参构造调无参构造
System.out.println("我是person类中的有参构造");
}
public void method(){
int num = 20;
System.out.println("num = " + num); //局部变量20
System.out.println("this.num = " + this.num); //全局成员变量10
System.out.println("我是person类中的method方法");
}
public void method1(){
System.out.println("我是person类中的method1方法");
}
}
java
package g;
public class Test {
public static void main(String[] args) {
Person person = new Person();
System.out.println("======================");
Person person1 = new Person(10);
}
}
继承的特点
-
继承只支持单继承,不能多继承
public class A extends B,C{} -> 错误
-
继承支持多层继承
public class A extends B{}
public class B extends C{}
-
一个父类可以有多个子类
public class A extends C{}
public class B extends C{}
-
构造方法不能继承,也不能重写
私有方法可以继承,但是不能被重写
静态方法可以继承,但是不能被重写
如何为父类中private的成员变量赋值(经验值)
-
利用javabean的set方法赋值
-
利用构造方法赋值
java
package h;
public class Employee {
private String name;
private int age;
public Employee(){}
public Employee(String name, int age){
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public void work(){
System.out.println("我要工作");
}
}
java
package h;
public class Manager extends Employee{
public Manager(){
}
public Manager(String name,int age){
super(name,age);
}
}
java
package h;
public class Test {
public static void main(String[] args) {
Manager m1=new Manager();
m1.setName("zs"); //利用javabean的set方法赋值
m1.setAge(10);
System.out.println(m1.getName()+"..."+m1.getAge()); //zs...10
System.out.println("=======================");
Manager m=new Manager("ww",11); //利用构造方法赋值
System.out.println(m.getName()+"..."+m.getAge());
}
}
抽象
-
抽象类怎么来的?抽取共性方法,放到父类中,发现方法没法实现,因为每个子类对此方法的实现方式细节不一样。此时方法体说不清道不明,可以定义成抽象方法,抽象方法所在的类一定是抽象类。
-
关键字: abstract
-
抽象方法: 修饰符 abstract 返回值类型 方法名(参数);
-
抽象类: public abstract class 类名{}
-
注意:
a.抽象方法所在的类一定是抽象类
b.抽象类中不一定非得有抽象方法
c.子类继承父类之后,需要重写父类中所有的抽象方法,不然编译报错
d.抽象类不能new对象,只能通过new子类对象调用重写的方法
-
可以将抽象类看成是一类事物的标准,要求只要是属于这一类的,都必须要拥有抽象类中的方法,必须要给我实现,怎么证明拥有了,怎么证明实现了呢?-> 重写
至于这个方法怎么实现,就看子类重写之后怎么写方法体了
-
抽象类不能直接new对象,只能创建非抽象子类的对象
-
抽象类中不一定非得有抽象方法,但是抽象方法所在的类一定抽象类
-
抽象类的子类,必须重写父类中的所有抽象方法,否则,编译报错,除非该子类也是抽象类
-
抽象类中可以有成员变量,构造,成员方法
-
抽象类中可以有构造方法,是供子类创建对象时,初始化父类属性使用的
java
package i;
public abstract class Animal {
public abstract void eat();
public abstract void drink();
}
java
package i;
public class Dog extends Animal{
@Override
public void eat() {
System.out.println("狗吃饭");
}
@Override
public void drink() {
System.out.println("狗喝水");
}
}
java
package i;
public class Cat extends Animal{
@Override
public void eat() {
System.out.println("猫吃饭");
}
@Override
public void drink() {
System.out.println("猫喝水");
}
}
java
package i;
public class Test {
public static void main(String[] args) {
Dog dog = new Dog();
dog.drink();
Cat cat = new Cat();
cat.eat();
}
}
接口
接口的定义和使用
-
接口:是一个引用数据类型,是一种标准,规则
-
关键字:
a.interface 接口: public interface 接口名{}
b.implements 实现:实现类 implements 接口名{}
-
接口中可以定义的成员:
a.jdk7以及之前:
-
抽象方法: public abstract -> 即使不写public abstract,默认也有
-
成员变量:public static final 数据类型 变量名 = 值-> 即使不写public static final,默认也有
-
final是最终的,被final修饰的变量不能二次赋值,所以我们一般将final修饰的变量视为常量
b.jdk8:
默认方法:public default 返回值类型 方法名(形参){}
静态方法:public static 返回值类型 方法名(形参){}
c.jdk9开始:
私有方法:private的方法
-
-
定义格式:
public abstract 返回值类型 方法名(形参);
-
实现:
public class 实现类类名 implements 接口名{}
-
使用:
a.实现类实现接口
b.重写接口中的抽象方法
c.创建实现类对象(接口不能直接new对象)
d.调用重写的方法
java
public interface USB {
public abstract void open();
public abstract void close();
}
java
public class Mouse implements USB{
@Override
public void open() {
System.out.println("鼠标打开");
}
@Override
public void close() {
System.out.println("鼠标关闭");
}
}
java
public class Main {
public static void main(String[] args) {
Mouse m = new Mouse();
m.open();
m.close();
}
}
接口中的成员
抽象方法
- 定义格式:
public abstract 返回值类型 方法名(形参); - 注意:
不写public abstract 默认也有 - 使用:
a.定义实现类,实现接口
b.重写抽象方法
c.创建实现类对象,调用重写的方法
默认方法
- 格式:
public default 返回值类型 方法名(形参){
方法体
return 结果
} - 使用:
a.定义实现类,实现接口
b.默认方法可重写,可不重写
c.创建实现类对象,调用默认方法
java
package b;
public interface USB {
public default void methoddef() {
System.out.println("我是默认方法");
}
}
java
package b;
public class Mouse implements USB{
@Override
public void methoddef() {
System.out.println("我是重写接口中的默认方法");
}
}
java
package b;
public class Main {
public static void main(String[] args) {
Mouse m = new Mouse();
m.methoddef();
}
}
静态方法
-
定义格式:
public static 返回值类型 方法名(形参){
方法体
return 结果
}
-
使用:
接口名直接调用,接口.方法
java
package c;
import org.w3c.dom.ls.LSOutput;
public interface USB {
public static void methodsta(){
System.out.println("我是接口中的静态方法");
}
}
java
package c;
public class Main {
public static void main(String[] args) {
USB.methodsta();
}
}
默认方法和静态方法,可以作为临时加的一个小功能来使用
成员变量
- 格式:
public static final 数据类型 变量名 = 值 - 相关知识点final:final代表最终的,被它修饰的变量,不能二次赋值,可以视为常量
- 特点:不写public static final 默认也有
- 使用:接口名直接调用
- 注意:
a.被static final修饰的成员变量需要手动赋值
b.习惯上我们会将static final修饰的成员变量名大写
java
package d;
public interface USB {
public static final int NUM1 = 10;
int num2 = 20;
//int num3; 报错,未赋值
}
java
package d;
public class Tet {
public static void main(String[] args) {
System.out.println("USB.num1 = " + USB.NUM1);
System.out.println("USB.num2 = " + USB.num2);
}
}
接口的特点
-
接口可以多继承 -> 一个接口可以继承多个接口
public interface InterfaceA extends InterfaceB,InterfaceC{}
-
接口可以多实现 -> 一个实现类可以实现一个或者多个接口
public class InterfaceImpl implements InterfaceA,InterfaceB{}
-
一个子类可以继承一个父类的同时实现一个或者多个接口
public class Zi extends Fu implements InterfaceA,InterfaceB{}
-
注意:继承也好,实现接口也罢,只要是父类中或者接口的抽象方法,子类或者实现类都要重写
-
当一个类实现多个接口时,如果接口中的抽象方法有重名且参数一样的,只需要重写一次
-
当一个类实现多个接口时,如果多个接口中默认方法有重名的,且参数一样的,必须重写一次默认方法
java
package e;
public interface interfaceA {
public abstract void method();
public default void methoddef() {
System.out.println("我是接口A中的默认方法");
}
}
java
package e;
public interface interfaceB {
public abstract void method();
public default void methoddef(int a) {
System.out.println("我是接口B中的默认方法");
}
}
java
package e;
public class InterfaceImpl implements interfaceA,interfaceB{
@Override
public void method() {
System.out.println("我是重写的method");
}
//不重写也行,因为方法有参数区分
// public void methoddef() {
// System.out.println("重写后的默认方法");
// }
}
java
package e;
public class Test {
public static void main(String[] args) {
InterfaceImpl impl = new InterfaceImpl();
impl.methoddef(); //我是接口A中的默认方法
impl.methoddef(10); //我是接口B中的默认方法
}
}
接口和抽象类的区别
相同点:
a.都位于继承体系的顶端,用于被其他类实现或者继承
b.都不能new
c.都包含抽象方法,其子类或者实现类都必须重写这些抽象方法
不同点:
a.抽象类:一般作为父类使用,可以有成员变量,构造,成员方法,抽象方法等
b.接口:成员单一,一般抽取接口,抽取的都是方法,视为功能的大集合
c.抽象类不能多继承,但是接口可以
多态
- 面向对象三大特征:封装、继承、多态
- 怎么学:
a.不要从字面意思上理解多态这两个字,要从使用形式上掌握
b.要知道多态的好处
c.要知道多态的前提
多态的介绍
- 前提:
a.必须有子父类继承或者接口实现关系
b.必须有方法的重写(没有重写,多态没有意义),多态主要玩儿的是重写方法
c.new对象:父类引用指向子类对象:Fu fu = new Zi() -> 理解为大类型接收了一个小类型的数据 ->比如 double b = 10 - 注意:多态前提下不能直接调用子类特有功能
java
package a;
public abstract class Animal {
public abstract void eat();
}
java
package a;
public class Dog extends Animal {
@Override
public void eat() {
System.out.println("狗啃骨头");
}
public void door() {
System.out.println("特有方法");
}
}
java
package a;
public class Cat extends Animal {
@Override
public void eat() {
System.out.println("猫吃鱼");
}
public void catchmouse (){
System.out.println("猫捉老鼠");
}
}
java
package a;
public class Test {
public static void main(String[] args) {
//原始方式
Dog dog = new Dog();
dog.eat(); //重写
dog.door(); //特有的
Cat cat = new Cat();
cat.eat(); //重写
cat.catchmouse(); //特有的
System.out.println("==================");
//多态形式
Animal animal = new Dog();
animal.eat(); //接收的dog对象,所以调用的是dog中的eat
//animal.door(); 多态前提下,不能直接调用子类特有成员
}
}
多态条件下成员的访问特点
成员看左边,方法看右边
成员变量
成员方法
java
package b;
public class Fu {
int num = 1000;
public void method(){
System.out.println("父类的成员方法");
}
}
java
package b;
public class Zi extends Fu{
int num = 10;
public void method(){
System.out.println("子类的成员方法");
}
}
java
package b;
public class Test {
public static void main(String[] args) {
Fu f = new Zi();
System.out.println("f.num = " + f.num); //1000,成员看左边,方法看右边
f.method(); //子类的成员方法,成员看左边,方法看右边
}
}
多态的好处(为什么学多态)
-
问题描述: 如果使用原始方式new对象(等号左右两边一样),既能调用重写的,还能调用继承的,还能调用自己特有的成员;但是多态方式new对象,只能调用重写的,不能直接调用子类特有的成员,那为啥还要用多态呢?
-
多态方式和原始方式new对象的优缺点:
原始方式:
a.优点:既能调用重写的,还能调用父类非私有的,还能调用自己特有的
b.缺点:扩展性差
多态方式:
a.优点:扩展性强
b.缺点:不能直接调用子类特有功能
Fu fu = new Zi()
double b = 10;
b = 100L;
-
形参传递父类类型,调用此方法父类类型可以接收任意它的子类对象
传递哪个子类对象,就指向哪个子类对象,就调用哪个子类对象重写的方法
java
public class Test02 {
public static void main(String[] args) {
/*
double b = 10;
b = 100L;
*/
Animal animal = new Dog();
animal.eat();
animal = new Cat();
animal.eat();
System.out.println("=================");
Dog dog = new Dog();
method(dog);
Cat cat = new Cat();
method(cat);
}
/*
形参传递父类类型,调用此方法父类类型可以接收任意它的子类对象
传递哪个子类对象,就指向哪个子类对象,就调用哪个子类对象重写的方法
*/
public static void method(Animal animal){//Animal animal = dog Animal animal = cat
animal.eat();
}
}
多态中的转型
向上转型
父类引用指向子类对象:double b = 1;
向下转型
-
向下转型:好比强转,将大类型强制转成小类型
-
表现方式:
父类类型 对象名1 = new 子类对象() -> 向上转型 -> double b = 1
子类类型 对象名2 = (子类类型)对象名1 -> 向下转型 -> int i = (int)b
-
想要调用子类特有功能,我们就需要向下转型
java
package a;
public class Test {
public static void main(String[] args) {
//原始方式
Dog dog = new Dog();
dog.eat(); //重写
dog.door(); //特有的
System.out.println("==================");
//多态形式
Animal animal = new Dog();
animal.eat(); //接收的dog对象,所以调用的是dog中的eat
//animal.door(); 多态前提下,不能直接调用子类特有成员
//向下转型
Dog dog1 = (Dog) animal;
dog1.eat();
dog1.door();
}
}
转型可能会出现的问题
-
如果等号左右两边类型不一致,会出现类型转换异(ClassCastException)
-
解决:在向下转型之前,先判断类型
-
怎么判断类型: instanceof
判断结果是boolean型
-
使用:
对象名 instanceof 类型 -> 判断的是关键字前面的对象是否符合关键字后面的类型
java
public class Test01 {
public static void main(String[] args) {
Dog dog = new Dog();
method(dog);
System.out.println("===============");
Cat cat = new Cat();
method(cat);
}
public static void method(Animal animal){//animal = dog animal = cat
/* animal.eat();
*//*
这里会出现类型转换异常(ClassCastException)
原因:当调用method,传递Cat对象时,animal代表的就是cat对象
此时我们将代表cat对象的animal强转成了dog
此时等号左右两边类型不一致了,所以出现了类型转换异常
*//*
Dog dog = (Dog) animal;
dog.lookDoor();*/
if (animal instanceof Dog){
Dog dog = (Dog) animal;
dog.eat();
dog.lookDoor();
}
if (animal instanceof Cat){
Cat cat = (Cat) animal;
cat.eat();
cat.catchMouse();
}
}
}
权限修饰符
在Java中提供了四种访问权限,使用不同的访问权限修饰符修饰时,被修饰的内容会有不同的访问权限,
-
public:公共的,最高权限,被public修饰的成员,在哪里都能访问
-
protected:受保护的
-
default::默认的 注意 不写权限修饰符就是默认权限,不能直接把default写出来
-
private:私有的,只能在自己的类中直接访问
我们只需要知道一个成员被这四个权限修饰符修饰在4种情况下能不能访问就行了
| public | protected | default(空的) | private | |
|---|---|---|---|---|
| 同类 | yes | yes | yes | yes |
| 同包不同类 | yes | yes | yes | no |
| 不同包子父类 | yes | yes | no | no |
| 不同包非子父类 | yes | no | no | no |
编写代码时,如果没有特殊的考虑,建议这样使用权限:
- 属性:用private -> 封装思想
- 成员方法public -> 便于调用
- 构造public -> 便于new对象
final关键字
-
概述:最终的
-
使用:
a.修饰一个类
b.修饰一个方法
c.修饰一个局部变量
d.修饰一个成员变量
e.修饰一个对象
-
怎么学final: 只需要知道被final修饰之后特点是啥即可
final修饰类
- 格式: public final class 类名{}
- 特点:被final修饰的类不能被继承
final修饰方法
-
格式:
修饰符 final 返回值类型 方法名(形参){
方法体
return 结果
}
-
特点:被final修饰的方法,不能被重写
-
注意:final和abstract不能同时修饰一个方法
final修饰局部变量
- 格式:final 数据类型 变量名 = 值
- 特点:被final修饰的变量不能二次赋值,相当于常量
final修饰对象
- 格式:final 数据类型 对象名 = new 对象();
- 特点:被final修饰的对象,地址值不能改变,但是对象中的属性值可以改变
java
package c;
public class Test {
public static void main(String[] args) {
Person person1 = new Person("zs",26);
System.out.println("person1 = " + person1); //c.Person@6d311334
Person person2 = new Person("zs",27);
System.out.println("person2 = " + person2); //c.Person@682a0b205
final Person person3 = new Person("ww",26);
System.out.println("person3 = " + person3);
//person3 = new Person("ww",18); 被final修饰的对象,地址值不能改变,但是对象中的属性值可以改变
person3.setName("ww");
person3.setAge(18);
}
}
final修饰成员变量
- 格式:final 数据类型 变量名 = 值
- 特点:
a.需要手动赋值
b.不能二次赋值
java
public class Student {
final String name = "广坤";
public Student() {
}
//有参构造现在属于二次赋值了
/*public Student(String name) {
this.name = name;
}*/
public String getName() {
return name;
}
//set方法现在属于二次赋值了
/*public void setName(String name) {
this.name = name;
}*/
}
代码块
构造代码块
-
格式:
{
代码
}
-
执行特点:优先于构造方法执行,每new一次,就会执行一次
java
package d;
public class Person {
public Person(){
System.out.println("我是无参构造");
}
{
System.out.println("我是代码块");
}
}
java
package d;
public class Test {
public static void main(String[] args) {
Person person1 = new Person(); //我是代码块 我是无参构造
}
}
静态代码块【重要】
-
格式:
static{
代码
}
-
执行特点: 静态代码块优先于构造代码块和构造方法执行的,而且只执行一次
-
使用场景:如果想让一些数据最先初始化,而且只需要初始化一次,就可以将这些数据放到静态代码块中,如java项目代码写连接数据库驱动的jdbc代码
java
package d;
public class Person {
public Person(){
System.out.println("我是无参构造");
}
{
System.out.println("我是代码块");
}
static {
System.out.println("我是静态代码块");
}
}
java
package d;
public class Test {
public static void main(String[] args) {
//Person person1 = new Person(); //我是代码块 我是无参构造
Person person2 = new Person(); //我是静态代码块 我是代码块 我是无参构造
Person person3 = new Person(); //我是代码块 我是无参构造
}
}
内部类
-
什么时候使用内部类:
当一个事物的内部,还有一个部分需要完整的结构去描述,而这个内部的完整结构又只为外部事物提供服务,那么整个内部的完成结构最好使用内部类。比如:人类都有心脏,人类本身需要用属性,行为去描述,那么人类内部的心脏也需要心脏特殊的属性和行为去描述,此时心脏就可以定义成内部类,人类中的一个心脏类
-
在java中允许一个类的定义位于另外一个类内部,前者就称之为内部类,后者称之为外部类
java
类A就是类B的外部类,类B就是类A的内部类
class A{
class B{
}
}
- 分类:
成员内部类(静态,非静态)
局部内部类
匿名内部类(重点)
静态成员内部类 / 非静态成员内部类
-
格式:直接在定义内部类的时候加上static关键字
public class A{
static class B{
}
}
-
注意:
a.内部类可以定义属性,方法,构造等
b.静态内部类可以被final或者abstract修饰,被final修饰之后,不能被继承,被abstract修饰之后,不能new
c.静态内部类不能调用外部的非静态成员
d.内部类还可以被四种权限修饰符修饰
-
调用静态内部类成员:外部类.内部类 对象名 = new 外部类.内部类()
-
调用非静态内部类成员:外部类.内部类 对象名 = new 外部类().new 内部类()
java
package a;
public class Person {
public void eat(){
System.out.println("人要干饭");
}
static class Heart{
public void jump(){
System.out.println("心脏跳动");
}
}
class Heart1{
public void jump(){
System.out.println("心脏跳动1");
}
}
}
java
package a;
public class Test {
public static void main(String[] args) {
Person person1 = new Person();
person1.eat();
//调用静态内部类成员:外部类.内部类 对象名 = new 外部类.内部类()
Person.Heart heart = new Person.Heart();
heart.jump();
//调用非静态内部类成员:外部类.内部类 对象名 = new 外部类.内部类()
Person.Heart1 heart1 = new Person().new Heart1();
heart1.jump();
}
}
- 外部类的成员变量和内部类的成员变量以及内部类的局部变量重名时,怎么区分?
java
package b;
public class Student {
String name = "金莲";
class Heart{
String name = "大浪";
public void display(String name){
System.out.println("name = " + name); //就近原则,内部类的局部变量
System.out.println("this.name = " + this.name); //内部类的成员变量
System.out.println("Student.this.name = " + Student.this.name); //外部类的成员变量
}
}
}
java
package b;
public class Test {
public static void main(String[] args) {
Student.Heart heart = new Student().new Heart();
/*
name = zs
this.name = 大浪
Student.this.name = 金莲
*/
heart.display("zs");
}
}
局部内部类
局部内部类基本操作
可以定义在方法中,代码块中,构造中
java
package c;
public class Person {
public void eat() {
class Heart {
public void jump(){
System.out.println("心脏跳动");
}
}
new Heart().jump();
}
}
java
package c;
import b.Student;
public class Test {
public static void main(String[] args) {
Person person = new Person();
person.eat(); //心脏跳动
}
}
局部内部类实际操作
接口类型作为方法参数传递和返回
-
接口作为方法参数,传递实参时,传递的是实现类对象
-
接口作为返回值类型返回,实际返回的是实现类对象
java
package d;
public interface USB {
public abstract void open();
}
java
package d;
public class Mouse implements USB {
public void open() {
System.out.println("鼠标打开");
}
}
java
package d;
public class Test {
public static void main(String[] args) {
Mouse mouse = new Mouse();
mouse.open();
method(mouse);
System.out.println("===================");
USB usb = method01(); //USB usb = new Mouse();
usb.open();
}
/*
接口作为方法参数,传递实参时,传递的是实现类对象
*/
public static void method(USB usb) { //USB usb = mouse ->多态
usb.open();
}
/*
接口作为返回值类型返回,实际返回的是实现类对象
*/
public static USB method01(){
Mouse mouse = new Mouse();
return mouse;
}
}
抽象类作为方法参数和返回值
-
抽象类作为方法参数传递,传递实参时,传递的是其子类对象
-
抽象类作为方法返回值类型返回时,实际返回的是其子类对象
java
package e;
public abstract class Animal {
public abstract void eat();
}
java
package e;
public class Dog extends Animal {
@Override
public void eat() {
System.out.println("狗吃骨头");
}
}
java
package e;
public class Test {
public static void main(String[] args) {
Dog dog = new Dog();
method01(dog);
System.out.println("=========================");
Animal animal = method02(); //Animal animal = dog
animal.eat();
}
public static void method01(Animal animal){ //Animal animal = dog
animal.eat();
}
public static Animal method02(){
Dog dog = new Dog();
return dog;
}
}
普通类做方法参数和返回值
-
普通类作为方法参数传递,传递的是对象
-
普通类作为方法返回值返回,返回的是对象
java
package f;
public class Person {
public void eat() {
System.out.println("吃饭");
}
}
java
package f;
public class Test {
public static void main(String[] args) {
Person person = new Person();
method01(person);
System.out.println("====================");
Person person2 = method02();
person2.eat();
}
public static void method01(Person person) {
person.eat();
}
public static Person method02() {
Person person = new Person();
return person;
}
}
局部内部类实际操作
java
package g;
public interface USB {
void open();
}
java
package g;
public class Test {
public static void main(String[] args) {
USB usb = method();
usb.open();
}
public static USB method(){
class Mouse implements USB {
@Override
public void open() {
System.out.println("鼠标打开");
}
}
return new Mouse();
}
}
匿名内部类【重点】
-
所谓的匿名内部类,可以理解为没有显式声明出类名的内部类。
-
问题描述:我们如果想实现接口,简单使用一次抽象方法,我们就需要创建一个实现类,实现这个接口,重写抽象方法,还要new实现类对象,所以我们在想如果就单纯的想使用一次接口中的方法,我们能不能不这么麻烦呢?可以
a.创建实现类,实现接口
b.重写方法
c.创建实现类对象
d.调用方法
-
如果就想单纯的使用一下接口中的方法,我们就没必要经过以上四步了,我们可以四合一
-
匿名内部类怎么学:就按照一种格式来学,这一种格式就代表了实现类对象或者子类对象
-
格式:
java
new 接口/抽象类(){
重写方法
}.重写的方法();
==============================
类名 对象名 = new 接口/抽象类(){
重写方法
}
对象名.重写的方法();
java
package h;
public interface USB {
void open();
void close();
}
java
package h;
public class Test {
public static void main(String[] args) {
new USB(){
public void open(){
System.out.println("打开");
}
@Override
public void close() {
System.out.println("关闭");
}
}.open();
System.out.println("====================");
USB usb = new USB() {
@Override
public void open() {
System.out.println("打开");
}
@Override
public void close() {
System.out.println("关闭");
}
};
usb.open();
usb.close();
}
}
- 匿名内部类复杂用法_当参数传递
java
package i;
public interface USB {
void open();
}
java
package i;
public class Test {
public static void main(String[] args) {
method01(new USB() {
@Override
public void open() {
System.out.println("打开");
}
});
}
public static void method01(USB usb){
usb.open();
}
}
- 匿名内部类复杂用法_当返回值返回
java
package i;
public class Test {
public static void main(String[] args) {
USB usb = method01();
usb.open();
}
public static USB method01(){
return new USB() {
@Override
public void open() {
System.out.println("打开");
}
};
}
}
API文档
-
什么叫做API:Application Programming Interface,简称API,又称之为应用编程接口,说白了:定义出来的类以及接口,以及其中的方法等。
-
为了方便我们去查询开发好的接口以及类,以及其中的方法,会对应提供一个文档 -> API文档
-
API文档作用:查询我们要使用的对象,以及方法,是我们程序员的"字典"
异常
- 概述:代码出现了不正常的现象;在java中,异常都是一个一个的类
java
import java.text.SimpleDateFormat;
import java.util.Date;
public class Demo01 {
public static void main(String[] args) {
//错误Error:StackOverflowError
method();
//运行时异常:ArrayIndexOutOfBoundsException
int [] arr1 = new int [3];
System.out.println("arr1[4] = " + arr1[4]);
/*
编译时期异常:ParseException
注意看:编译时期异常是我们代码写错了嘛?不是,当我们调用方法的时候
该方法底层给我们抛了一个编译时期异常,所以导致我们一调用此方法一编译,就爆红了
当我们一旦触发了这个异常,jvm就会将异常信息打印到控制台上,给程序员们看
*/
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-DD HH:mm:ss");
String time = "2000-10-10 10:10:10";
Date date = sdf.parse(time);
System.out.println("date = " + date);
}
public static void method(){
method();
}
}
创建异常对象(了解)
- 创建异常对象,只是为了后面学习如何处理异常,其他的暂时没有啥意义
- 关键字:throw
- 格式: throw new 异常
java
public class Demo02 {
public static void main(String[] args) {
String s = "a.txlt";
method(s);
}
public static void method(String s){
if(!s.endsWith(".txt")){
//故意创建异常对象,用throw说明此处有异常
throw new NullPointerException();
}
System.out.println("我要输出");
}
}
异常处理方式(重点)
异常处理方式一_throws
- 格式:在方法参数和方法体之间位置上写 throws 异常
- 意义:处理异常,将异常往上抛;一个功能废了,其他都受影响
java
import java.io.FileNotFoundException;
import java.text.SimpleDateFormat;
import java.util.Date;
public class Demo01 {
public static void main(String[] args) throws FileNotFoundException {
String s = "a.txt1";
add(s);
delete(s);
update(s);
find(s);
}
public static void add(String s) throws FileNotFoundException {
if(!s.endsWith(".txt")){
throw new FileNotFoundException("文件找不到");
}
System.out.println("增加功能");
}
public static void delete(String s){
System.out.println("删除功能");
}
public static void update(String s){
System.out.println("修改功能");
}
public static void find(String s){
System.out.println("查找功能");
}
}
异常处理方式一_throws多个异常
-
格式: throws 异常1,异常2 ;一个功能废了,其他都受影响
-
注意:
如果throws的多个异常之间有子父类继承关系,我们可以直接throws父类异常
如果不知道多个异常之间是否有子父类继承关系,我们可以直接throws Exception
java
import java.io.FileNotFoundException;
import java.io.IOException;
public class Demo03 {
public static void main(String[] args) throws IOException{
String s = "a.txt1";
add(s);
delete(s);
update(s);
find(s);
}
//IOException为父类,FileNotFoundException为子类,所以FileNotFoundException 写不写无所谓
//如果不知道多个异常之间是否有子父类继承关系,我们可以直接throws Exception
public static void add(String s) throws IOException,FileNotFoundException {
if(s==null){
throw new IOException("IO异常");
}
if(!s.endsWith(".txt")){
throw new FileNotFoundException("文件找不到");
}
System.out.println("增加功能");
}
public static void delete(String s){
System.out.println("删除功能");
}
public static void update(String s){
System.out.println("修改功能");
}
public static void find(String s){
System.out.println("查找功能");
}
}
异常处理方式二_try...catch
- 格式: 一个功能废了,其他不受影响
java
try{
可能出现异常的代码
}catch(异常 对象名){
处理异常的代码-> 将来开发会将异常信息保存到日志文件中
}
java
import java.io.FileNotFoundException;
import java.io.IOException;
public class Demo04 {
public static void main(String[] args) {
String s = "a.txt1";
try{
int [] arr = null;
System.out.println(arr.length); //NullPointerException,try catch抓不到,但父类Exception e可以抓到
add(s);
}catch(FileNotFoundException e){ //也可以写父类Exception e
System.out.println(e);
}
delete(s);
update(s);
find(s);
}
public static void add(String s) throws FileNotFoundException {
if(!s.endsWith(".txt")){
throw new FileNotFoundException("文件找不到");
}
System.out.println("增加功能");
}
public static void delete(String s){
System.out.println("删除功能");
}
public static void update(String s){
System.out.println("修改功能");
}
public static void find(String s){
System.out.println("查找功能");
}
}
异常处理方式二_多个catch
- 格式:
java
try{
可能出现异常的代码
}catch(异常 对象名){
处理异常的代码-> 将来开发会将异常信息保存到日志文件中
}catch(异常 对象名){
处理异常的代码-> 将来开发会将异常信息保存到日志文件中
}catch(异常 对象名){
处理异常的代码-> 将来开发会将异常信息保存到日志文件中
}catch(异常 对象名){
处理异常的代码-> 将来开发会将异常信息保存到日志文件中
}...
- 注意:
如果catch的多个异常之间有子父类继承关系,我们可以直接catch父类异常
如果不知道多个异常之间是否有子父类继承关系,我们也可以直接catch Exception
java
import java.io.FileNotFoundException;
import java.io.IOException;
public class Demo05 {
public static void main(String[] args) {
String s = "a.txt1";
try{
add(s);
}catch(FileNotFoundException e){ //也可以写只抓父类Exception e
e.printStackTrace(); //详细信息打印到控制台
}catch(IOException e){
System.out.println(e);
}
delete(s);
update(s);
find(s);
}
public static void add(String s) throws IOException,FileNotFoundException{
if(s==null){
throw new IOException("IO异常");
}
if(!s.endsWith(".txt")){
throw new FileNotFoundException("文件找不到");
}
System.out.println("增加功能");
}
public static void delete(String s){
System.out.println("删除功能");
}
public static void update(String s){
System.out.println("修改功能");
}
public static void find(String s){
System.out.println("查找功能");
}
}
finally关键字
- 概述:与Final完全不同,代表的是不管是否触发了异常,都会执行的代码块,最先执行
特殊情况:如果之前执行了System.exit(0)终止当前正在执行的java虚拟机 - 使用:都是配合try...catch使用
java
try{
可能出现异常的代码
}catch(异常 对象名){
处理异常的代码-> 将来开发会将异常信息保存到日志文件中
}finally{
不管是否有异常,都会执行的代码
}
java
import java.io.FileNotFoundException;
public class Demo06 {
public static void main(String[] args) {
String s = "a.txt1";
try {
add(s);
} catch (FileNotFoundException e) {
e.printStackTrace(); throw new RuntimeException(e);
}finally {
System.out.println("我必须输出");
}
}
public static void add(String s) throws FileNotFoundException {
if(!s.endsWith(".txt")){
throw new FileNotFoundException("文件找不到");
}
System.out.println("增加功能");
}
}
-
使用场景:关闭资源
原因:对象如果没有用了,GC(垃圾回收器)回收,用来回收堆内存中的垃圾,释放内存,但是有一些对象GC回收不了,比如:连接对象(Connection),IO流对象,Socket对象,这些对象GC回收不了,就需要我们自己手动回收,手动关闭。将来不能回收的对象new完之后,后续操作不管是否操作成功,是否有异常,我们都需要手动关闭,此时我们就可以将关闭资源的代码放到finally中。
java
public class Test {
public static void main(String[] args) {
FileWriter fw = null;
try {
fw = new FileWriter("day13_exception_object\\1.txt");
fw.write("哈哈哈");//假如这里写失败或者写成功了
} catch (IOException e) {
throw new RuntimeException(e);
}finally {
if (fw!=null){
try {
fw.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
}
}
继承中方法重写抛异常时注意的事项
- 如果父类中的方法抛了异常,那么子类重写之后要不要抛?可抛可不抛
- 如果父类中的方法没有抛异常,那么子类重写之后要不要抛? 不要抛
try_catch和throws的使用时机
-
如果处理异常之后,还想让后续的代码正常执行,我们使用try...catch
-
如果方法之间是递进关系(调用),我们可以先throws,但是到了最后需要用try...catch做一个统一的异常处理
-
编译时期异常是必须要处理的,不处理爆红,没法往下写
a.throws
b.try...catch
-
运行时期异常我们一般不处理,一旦出现运行时期异常,肯定是代码写的有问题,我们直接修改代码细节就行啦
自定义异常
- 需求:键盘录入一个用户名,实现登录功能,如果登录失败,抛出LoginUserException
-
- 定义一个类
- 如果继承Exception 就是编译时期异常
- 如果继承RuntimeException,就是运行时期异常
java
public class LoginUserException extends Exception{
public LoginUserException(){
}
public LoginUserException(String message){
super(message); //调用父类
}
}
java
import java.util.Scanner;
public class Demo08 {
public static void main(String[] args) throws LoginUserException {
//1. 定义一个用户名,代表已经注册的用户
String username = "root";
Scanner sc = new Scanner(System.in);
System.out.println("清输入您要登录的用户名:");
String name = sc.next();
//判断用户名是否和已经存在的用户名一致
if(name.equals(username)){
System.out.println("登录成功");
}else{
throw new LoginUserException("登录失败");
}
}
}
打印异常信息的三个方法
Throwable类中的方法:
String toString() :输出异常类型和设置的异常信息
String getMessage(): 输出设置的异常信息
void printStackTrace():打印异常信息是最全的:包括异常类型,信息,以及出现的行数等
java
import java.util.Scanner;
public class Demo08 {
public static void main(String[] args){
//1.定义一个用户名,代表已经注册的用户
String username = "root";
//2.创建Scanner对象,录入用户名
Scanner sc = new Scanner(System.in);
System.out.println("请您输入要登录的用户名:");
String name = sc.next();
//3.判断用户名是否和已经存在的用户名一致
if (name.equals(username)) {
System.out.println("登录成功了");
} else {
try {
throw new LoginUserException("登录失败了,用户名或者密码有问题");
}catch (Exception e){
System.out.println(e.toString()); //LoginUserException: 登录失败了,用户名或者密码有问题
System.out.println(e.getMessage()); //登录失败了,用户名或者密码有问题
e.printStackTrace(); //LoginUserException: 登录失败了,用户名或者密码有问题 at Demo08.main(Demo08.java:16)
}
}
}
}