新职课(chapter3)

面向对象

  • 一个.java文件可以有N个类,但只能有一个public
  • 类包含属性(特征)和方法(行为)
  • 函数的返回
    void say(){
        if (true){
            return;	// 如果进入这里,return表示方法执行结束,无需向下执行,不返回任何值(也不能)
        }
        System.out.println("false");
    }
    
  • 栈内存:通过栈指针来创建空间和释放空间,速度很快!
    • 栈中存储对象/变量名称,并指向堆中的对象
  • 堆内存:存放类的实例(对象)
    • Java是纯面向对象语言,类的对象必须是通过new创建
  • 方法区:类中包含的方法会放在方法区
    • 因为方法都是一样的,不需要每次都实例化;通过堆中的对象调用,传入堆中的属性即可
  • 对象使用完毕栈会先后释放,堆中的对象没有引用指向,符合GC机制,被回收!
  • 如果定义了有参数构造方法,必须自定义无参构造
    • 建议任何时候都自定义无参和全参构造方法

方法重载

  • 类中定义的方法,如果要重载,需满足:
    • 方法名称相同
    • 参数列表长度或参数列表类型不同,或者参数类型的顺序不同
    class Mathematics{
        int sum(int x, int y){
            return x+y;
        }
    
        double sum(int x, double y){
            return x+y;
        }
        
        double sum(double x, int y){
            return x+y;
        }
    }
    
  • 最常见的是构造方法的重载,各种参数长度
  • 匿名对象:不给变量接收,只用一次

编程规约

  • 命名风格
    1
    2

三大特性

封装

  • 隐藏对象的属性和实现细节,只暴露出公共的方法,里面可以添加约束
    • 一般就是讲属性设置为private
    • 设置setget方法;IDEA右键—Generate—Getter and Setter
  • 构造方法中,如果参数和属性不重名,编译器默认隐藏了this(表示当前对象)
    • 构造方法只需要public,不需要返回值类型
    class Person{
        private String name;
        private int age;
    
        Person(){
            this("Roy", 22);	// 调用自己的有参构造(不推荐)
        }
    
        Person(String name, int age){
            this.name = name;
            this.age = age;
        }
    }
    
  • 使用static修饰的属性和方法都存放在公共方法区
    • 可以将private修饰的属性理解为对象属性/对象方法
    • 静态属性理解为类属性/类方法,在类加载时创建一份,可直接使用类名调用
    • 优点:对象可以创建多个,类只有一份,可以放置所有对象的公共属性/方法节省内存
    class Student{
        private String name;
        private static String region;   // 学生地区,都是一样的
    
        Student(String name){
            this.name = name;
        }
    
        public static void say(){	// 或者get方法
            System.out.println("这是static方法!");
            // return region;
        }
    
        public static void main(String[] args) {
            Student.say();
        }
    }
    
  • 类先加载,对象才创建,所以对象方法可以调用静态方法,反之不行!

  • 把功能相同或相关的类或接口写在同一个包package中,例如可以叫做com.roykun
  • 不同包的类名可以相同,导入类需要加上包名作为区分,例如import com.roykun.Students;
  • 包也可以限定访问权限
  • 有一些类不需要导包,例如package java.lang;,经常使用,系统自动导入;当然,使用当前包中的其他类也不需要

构造代码块

  • 随着对象的创建,执行一次,且在构造方法之前
  • 构造方法不一定执行(重载),构造代码块一定会执行
  • 构造代码块可以有多个
    class GouZao{
        private String name;
    
        {
            System.out.println("构造代码块1");
        }
    
        {
            System.out.println("构造代码块2");
        }
    
        GouZao(){
            System.out.println("构造方法");
        }
    
        public static void main(String[] args) {
            GouZao gz = new GouZao();
        }
    }
    // 构造代码块1
    // 构造代码块2
    // 构造方法
    
  • 静态代码块
    • 只会随着类的加载,执行一次,而且先于构造代码块
    static {
       System.out.println("静态代码块");
    }
    
  • 输出错误提示可以使用:
    System.err.println("页数不能少于200~");
    

继承

  • 继承就是子类继承父类的属性和行为,方便代码的复用
  • Java中只有单继承、多重继承,没有多继承
  • 子类实例化时,会先创建父类(先执行的还是子类的构造,但是隐藏了第一行的super();),本质是子类拥有了父类的一个地址
    • 子类创建时,默认使用父类的无参构造方法创建父类
    • 如果在子类构造方法中制定父类的其他构造方法,需要写在第一行
  • 子类能够直接使用父类protectedpublic修饰的属性和方法,或者通过提供的set方法

重写

  • 重载(overload):一个类中,同名方法,参数长度、类型或顺序不同
  • 重写(override):
    • 发生在子类和父类中
    • 参数列表必须完全相同
    • 返回类型必须相同
    • 访问权限不能比父类中的权限低,例如父类使用public,子类就不能用protected(越开放权限越大)
    • 父类的成员方法只能被他的子类重写
    • 声明为static和private的方法不能重写,但能够再次声明
      • 从实际开发来讲,static没必要重写
      • private是因为权限子类拿不到,重写个锤子在这里插入图片描述

final

  • 用于修饰属性:
    • 修饰的局部变量只能赋值一次
    • 修饰的成员属性,必须在声明时赋值
    class Stu{
        final int a=0;
    
    	public static void main(String[] args) {
        	final int a;
    	}
    }
    public static final;	// 全局常量
    
    • 简而言之,将变量变为常量
    • 命名规范:所有单词大写,单词间用下划线隔开
  • 用于修饰类
    • 说明这个类不能被继承
  • 用于修饰方法
    • 不能被子类重写

抽象

  • 抽象类
    public abstract class Person {
        public abstract void abc(); // 抽象方法
    }
    
  • 继承抽象类,必须实现里面的所有抽象方法
    import com.book.Person;
    
    public class PersonTest extends Person {
        @Override
        public void abc() {
            System.out.println("实现抽象方法");
        }
    }
    
    • 抽象类不能实例化(不能new),因为没有定义内容啊
    • final不能修饰抽象类,因为抽象类必须被继承才有意义(只能public或者protected)
    • 抽象类也能有构造方法
  • 好处,只定义方法名称,具体内容交给子类实现,让父类更通用更简洁

接口

  • 类中全部都是抽象方法,属性都是全局常量
  • 面向接口编程:定义与实现的分离(解耦)
    • 降低程序耦合性
    • 易于程序的扩展
    • 有利于程序的维护
  • 因为都是全局常量,所以可以省略写public static final
  • 因为都是抽象方法,可以省略public abstract
    public interface Person{	// 文件名必须是Person.java
        int a= 0;
        void say();
    }
    
  • 继承接口:implement
public class PersonTest implements Person {
    @Override
    public void say() {
        System.out.println("实现接口方法");
    }
}
  • 和抽象类的区别:
    在这里插入图片描述
  • 简而言之,抽象类和普通类差别不大,但是接口纯抽象,纯常量

多态

  • 条件:有继承,有重写,父类引用指向子类对象
  • 举个例子:
    // Person.java
    public class Person {
        private String name;
        private int age;
    
        public void show(){
            System.out.println("Person");
        }
    }
    
    // Students.java
    public class Students extends Person{	// 继承
        private String name;
        private int age;
    
        public void show(){	// 重写
            System.out.println("Students");
        }
    }
    
    // Demo.java
    public class Demo {
        public static void main(String[] args) {
            Students s = new Students();
            say(s);	// Students  执行的是子类的方法
            // 只要是继承了父类,都可以传
        }
    
        public static void say(Person p){	// 全局函数:传递的是父类的引用,父类引用指向子类对象
            p.show();
            // say() 传父用子
        }
    }
    
  • 类有父类和子类之分,子类就是父类的其他形态,体现了对象的多态性
  • 方法的重载和重写也是一种多态的体现,即同名方法的不同形态
  • 多态的好处有很多
    • 符合面向接口编程的思想,各子类只需继承父类,单独设计好,自定义的子类就能作为参数向接口传递,不必修改全局函数的代码(传父用子
    • Java是单继承的(只能extends一个父类)
    • Java是多实现的(可以implements多个接口,因为是全局常量抽象方法,不用担心调用冲突)
  • 这里有可能需要判断传入的类型,避免转型出错
    if (p instanceof Students){	// p是Students类型
        Students s = (Students)p;   // 强转 
    }
    
    • 当 p 为 Students 的对象,或者是其直接或间接子类,或者是其接口的实现类,结果都返回 true

Object

  • 所有类的父类
  • 作为形参,可以用来接收任意的引用类型数据
  • 重写toString()方法
    在这里插入图片描述
    @Override
    public String toString() {
        return "Students{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
    
  • equals()方法
    • ==比较的是内存地址,但是equals方法默认就是用这个,一般得重写
    // Person.java
    public boolean equals(Object o){
        if (this == o){
            return true;    // 内存如果相同必相等
        }
        if (o == null){
            return false;   // 非空
        }
        if (o instanceof Person){
            Person p = (Person)o;   // 转型到Person比较(有可能是其子类)
            if (this.name.equals(p.name) && this.age == p.age){	// 需要根据业务调整
                return true;
            }
        }
        return false;
    }
    
    // 自动生成
    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;	// 涉及到反射
        Person person = (Person) o;
        return age == person.age && Objects.equals(name, person.name);
    }
    
  • 后面会说到转型的问题

API

  • JDK11API中文最新版
    3

内部类

  • 用的不多,将一个类定义在另一个类的内部
    • 成员内部类
    • 局部内部类
    • 匿名内部类
    • 静态内部类
  • 成员内部类
    • 可以无条件访问所有外部类的属性和方法
    • 如果和外部类同名,默认访问内部类方法
    // Outer
    public class Outer {
        private int x;
    
        public int getX() {
            return x;
        }
    
        public void setX(int x) {
            this.x = x;
        }
    
        class Inner{
            public void show(){
                System.out.println(x);  // 默认打印自己的
                System.out.println(Outer.this.x);
            }
        }
    }
    
    // Demo
    public class Demo {
        public static void main(String[] args) {
            Outer o = new Outer();
            o.setX(110);
            Outer.Inner i = o.new Inner();
            i.show();   // 200  110
        }
    }
    
  • 局部内部类
    • 定义在方法里的类,一般是这个类就用一次,才这么干
  • 匿名内部类:没有名字,直接跟在父类或接口后面定义
    Person p = new Person(){
    	int a = 10;
    	public void say(){
    		System.out.println(匿名内部类方法);
    	}
    }
    
    • 不能是抽象的,不能定义构造函数
    • 只能访问final型的局部变量
  • 静态内部类
    • 内部类class前多加了个static关键字
    • 不需要依赖外部类的对象,即可以不创建外部类,也不能使用外部类的非static属性和方法

包装类型

  • 基本数据类型声明时直接在栈中,其他的(引用、数据结构、数据类型)都属于类,会创建对象在堆中
  • 对基本类型封装了对应的包装类型
    4
  • Integer/Long/Double/Short/Float/Bytejava.lang.Number的子类
  • Character/Boolean直接是Object的子类
  • 装箱/拆箱
    5
    public static void main(String[] args) {
            Integer i = new Integer(100);   // 装箱
            int a = i.intValue();   // 拆箱
    
            Integer b = 200;  // 自动装箱
            int c = b;  // 自动拆箱
        }
    
    • 包装类的parse方法,可以将一个字符串变为指定的基本数据类型(得是相关字符串)
    Scanner in = new Scanner(System.in);
    String s = in.nextLine();   // 字符串 100
    int x = Integer.parseInt(s);    // 提取出里面的数字
    System.out.println(x+1);
    

可变参数

  • 参数长度不固定,使用...表示(无限制)
    public static void sum(int... nums){
        int n = 0;
        for (int i=0; i<nums.length; i++){
            n += nums[i];
        }
        System.out.println(n);
    }
    
  • 可变参数必须在其他参数的后面

递归

  • 实现阶乘
    public static int jiecheng(int n) {
        if (n==1){
            return 1;
        }else {
            return n*jiecheng(n-1);	// 5*4!...
        }
    }
    
  • 很好理解,每次函数的调用,只看它最终会得到的结果(就一个数),别去想它的过程

异常

  • 在API里搜索Exception就能看到所有的异常类
    • 非受检异常:例如RuntimeException,运行时传递的参数等不定原因引起的,JVM会自动处理,也可以捕获
    • 受检异常:必须抛出或捕获,明确处理方式,否则程序无法运行
  • 捕获异常
    • JVM会根据异常的情况,创建对象,包含了异常信息
    • 出现问题,为了不返回给调用的方法而引起程序终止,需要捕获
    public class Exp {
        public static void main(String[] args) {
            Scanner s = new Scanner(System.in);
            System.out.println("输入被除数:");
            int a = s.nextInt();
            System.out.println("输入除数:");
            int b = s.nextInt();
    
            try {
                System.out.println(a/b);
                System.out.println("计算完毕!");
            }catch (NumberFormatException e){
                // System.out.println(e.getMessage());
                System.out.println("除数不能为0");
            }finally {
                System.out.println("程序结束...");
            }
        }
    }
    
    • 可以设置多个catch块捕获异常,也可以用|的形式写,也可以直接使用RuntimeException(多态)
    • finally必执行,一般用来手动释放资源
  • 注意这里有个常考点:
    public static Person show2(){
        Person p = new Person();
        try {
            p.age = 22;
            return p;
        }catch (RuntimeException e){
            return null;
        }finally {
            p.age = 18;
        }
    }
    
    public static int show1(){
        int a = 10;
        try {
            return a;
        }catch (RuntimeException e){
            return 0;
        }finally {
            a = 20;
        }
    }
    
    public static void main(String[] args) {
        System.out.println(show1());	// 10
        System.out.println(show2().age);	// 18
    }
    
    • 这里finally中的代码是否会改变返回结果,得看操作的对象是栈中的普通变量,还是堆中的引用
    • return会复制栈中我们要返回的内容,如果是引用,那复制的就是内存地址,改了p.age,还是会改变最终结果;如果复制的只是一个值,自然不会变!

抛出

  • 一般捕获了异常都是要处理的,但有时需要抛出,因为不是这里代码的问题
    public static void shut(String text) throws IOException {
            Runtime.getRuntime().exec(text);	// 这里传参不对可能异常
        }
    
  • 如果是因为传参导致,应该通过throws将异常抛出去,用户的问题
  • 也可以自己实例化异常抛出去,用的很少

自定义

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Roy_Allen

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值