3.java基本语法(三):面向对象、异常

java面向对象

以类的方式组织代码,以对象的方式组织(封装)数据

三大特性:封装、继承、多态

1.对象声明

public class A {
    int num=0;
	//......
}

public class MainTest {

    public static void main(String[] args) {
        A x=new A();  
    }
}

new为x分配一个内存空间,A()构造一个对象,可以在A类中自定义A的构造器。

若类成员不加private/public关键字,将默认为public(C++中是private)

2.静态方法

//类A
public class A {
    public static void staticFunction(){
        System.out.println("This is a static function");
    }
    public void commonFunction(){
        System.out.println("This is a common function");
    }
}

//主类
public class MainTest {

    public static void main(String[] args) {
        A.staticFunction();   //静态方法可以通过类直接调用
        A x=new A();
        x.commonFunction();   //非静态方法必须通过实例调用
    }
}

静态方法也可以通过实例调用,不过会产生警告

如:Static member ‘Mitchell.A.staticFunction()’ accessed via instance reference。 IDE会建议只通过类来调用静态方法,可能是考虑到实例会被回收。

3.构造器

//Student.class
public class Student {
    private String id;
    private String name;
    private int age;
    private int score;
    
    public Student(String id,String name,int age,int score){
        this.id=id;
        this.name=name;
        this.age=age;
        this.score=score;
        System.out.println("学生信息记入成功");
    }
    public String makeString(){
        return "[ "+name+",学号:"+id+",年龄:"+age+",成绩:"+score+" ]";
    }
}
public class HelloWorld {

    public static void main(String[] args) {
        Student a=new Student("123456789","张三",19,90);
        System.out.println(a.makeString());
    }
}

IDEA中通过alt+insert键可以快速生成构造器以及其他常用方法。

与C++的相同之处:

  1. 如果在已经定义了带参构造器的情况下还想调用无参构造器,则必须在类中写出无参构造器的实现。

与C++的不同之处:

  1. java不用手动管理内存(有垃圾回收机制),不用像C++一样写出析构函数(以及使用delete关键字)。
  2. java构造器中,若成员变量和使用的参数命名冲突,则必须调用this来获取成员变量(如上述Student类中"this.id=id")。C++对这种情况不会报错。
  3. 若类成员不加private/public关键字,java将默认为public,C++则默认为private。

堆栈调用图解

方法区属于堆

在这里插入图片描述

4.封装

目的:高内聚,低耦合。高内聚就是类的内部数据操作细节自己完成,不允许外部干涉;低耦合指仅暴露少量的方法给外部使用。

与C++一样,私有成员若要通过对象操作,需要额外定义一些方法。

IDEA使用alt+insert中的getter和setter选项可以直接生成对特定私有成员变量的最简操作方法。

5.继承

java中只有单继承。(C++允许多继承)

java中所有类都默认继承Object类,所以即使是一个空的类也会内含一些从Object继承过来的方法。

java通过extends关键字表示继承。IDEA中通过ctrl+H可以打开继承树。

5.1 super

thissuper
代表的对象不同当前类的引用当前类的父类的引用
前提没有继承也可以使用必须有继承条件才能使用(一般都能用,因为都会继承自Object类)
构造方法this()本类的构造super()父类的构造

例:

//Person.java    父类
package Mitchell;

public class Person {
    public String name="UNKNOW";
    public int age=-1;

    public Person(){
    }

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }
}
//Student    子类
package Mitchell;

public class Student extends Person{
    public Student(){
        super();    //调用父类的无参构造方法
    }
    public Student(String name) {
        this.name=name;
    }
    public Student(int age){
        this.age=age;
    }
    /*public Student(String name, int age) {   //这个方法与下一个方法效果相同
        super(name, age);
    }*/
    public Student(String name,int age){
        this(name);   //调用当前类的第二个方法
        this.age=age;    
        //this(age);   
        //this()只能调用一次,因为它必须放在第一句,如果用了两个或两种this(),会报错
    }
}
//测试
package Mitchell;
import java.util.*;

public class HelloWorld {

    public static void main(String[] args) {
        Student x=new Student("张三",18);
        System.out.println("["+x.name+","+x.age+"]");
    }
}
//输出  [张三,18]

注:

  1. super调用父类的构造方法,必须放在构造方法中的第一句。
  2. super必须只出现在子类的方法或构造方法中。
  3. super和this不能同时用在同一个构造方法中。

可参考博客:https://blog.csdn.net/lncsdn_123/article/details/79025525

5.2 方法重写

重写原因:父类的功能,子类不一定需要,或不一定适合子类使用。

@Override 表示重写,是一个有功能的注释。IDEA中可以通过alt+insert–>override进行方法重载。

例:

//B.java   父类
public class B{
    public void test(){
        System.out.println("B-->test()");
    }
}
//A.java   子类
public class A extends B{
    
    @Override
    public void test(){
        System.out.println("A-->test()");
    }
}
//测试
public class MainTest{
    
    public static void main(String[] args){
        A a=new A();
        a.test();   //调用A的test()
        
        B b=new A();   //父类的引用指向了子类
        b.test();    //本来会调用B的test,但是当前的b指向的是类A创建的对象,且test()方法被类A重载了。
    }
}

//两个test都输出  "A-->test()"

重写方法注意:

  1. 方法名、参数列表必须完全相同。
  2. 修饰符:重载后范围可以扩大,但是不能缩小。 (范围:public>protected>default>private)
  3. 抛出的异常:重载后的异常范围可以缩小,但是不能扩大。如ClassNotFoundException<Exception

6.多态

同一方法可以根据发送对象的不同而采用多种不同的行为方式。

多态存在的条件:

  1. 有继承关系。
  2. 子类重写父类的方法。
  3. 父类引用指向子类的对象。

一个类的实例是确定的,但是指向这个实例的对象不是确定的。
假设Student继承了Person,则可以如下声明对象

Student s1=new Student();
Person s2=new Student();  //父类指向直接子类
Object s3=new Student();  //父类指向间接子类

//一个对象能使用哪些方法跟"="号左边的类型有关。
//如果同一个方法子类有重载,就会调用子类的方法,否则调用"="号左边类的相关方法。

父类不能直接调用子类独有的方法。

假设Student有一个方法 a(),而其父类Person没有。若父类对象要调用a(),可以如下表示

((Student)s2).a();   //把父类对象强制转换成子类对象

上述方式的强制转换只是临时的,如果接下来还想调用方法a()的话仍然要按上述方式。
当然还可以新建一个对象(假设为x)存储强制转换后的s2对象,这样x可以直接调用a()。

类型转换异常:ClassCastException。

带static、final、private关键字的方法不能被重写。

注:

  1. 父类引用可以指向子类的对象。
  2. 子类对象转换为父类对象为向上转型,可以自动执行,不过可能会丢失一些子类中的方法。
  3. 父类对象转换为子类对象为向下转型,需要执行强制转换。

7.static静态代码块

public class HelloWorld {

    {
        System.out.println("匿名代码块");
    }

    //1.最先执行,且只执行一次
    static{
        System.out.println("静态代码块");
    }

    //
    public HelloWorld(){
        System.out.println("构造方法");
    }

    public static void main(String[] args) {
        HelloWorld x1=new HelloWorld();
        System.out.println("================");
        HelloWorld x2=new HelloWorld();
    }
}

//输出:
//静态代码块
//匿名代码块
//构造方法
//================
//匿名代码块
//构造方法

8.抽象类

声明

public abstruct class a{
	public abstruct void function(); //抽象方法
}

抽象方法通过继承在非抽象子类中进行具体实现。

注:

  1. 抽象类不能构建实例。不过抽象类中可以写构造器。
  2. 抽象类中可以写普通方法。
  3. 抽象方法只能写在抽象类中。

9.接口

接口可以多继承

接口可以算作特殊的抽象类,它的内部只有抽象方法

声明

public interface UserServices{
	//抽象方法或其他成员
}

接口中的抽象方法不用写public abstruct这些关键字。直接写函数定义即可,
如:void function(int x);
接口默认为抽象方式

接口中也可以定义常量,一般用public static final修饰,不过用处不大。

接口通过关键字implement实现。

public class UserServiceimp implements Uservices{  //可以同时实现多个接口
	//......
}

10.内部类

更详细内容可参考博客:https://www.cnblogs.com/dolphin0520/p/3811445.html

10.1成员内部类

public class Test {
    public static void main(String[] args)  {
        //第一种方式:
        Outter outter = new Outter();
        Outter.Inner inner = outter.new Inner();   //必须通过Outter对象来创建

        //第二种方式:
        Outter.Inner inner1 = outter.getInnerInstance();
    }
}

class Outter {    //外部类
    private Inner inner = null;
    private int x=10;
    public Outter() {

    }

    public Inner getInnerInstance() {
        if(inner == null)
            inner = new Inner();
        return inner;
    }

    class Inner {   //内部类,可以访问外部类的private类型成员
        private int b=-1;     //内部类的private只能通过外部类访问,若为public则其他地方都可以直接访问
        public Inner() {
            
        }
    }
}

成员内部类可以无条件访问外部类的所有成员属性和成员方法(包括private成员和静态成员)。

10.2局部内部类

class People{
    public People() {
         
    }
}
 
class Man{
    public Man(){
         
    }
     
    public People getWoman(){
        class Woman extends People{   //局部内部类,当前的类只能访问getWoman()作用域内的资源
            int age =0;
        }
        return new Woman();
    }
}

局部内部类的访问仅限于方法内或者该作用域内。

局部内部类不能加private等关键字修饰它的成员。

10.3匿名内部类

这种类不需要提供类名就能直接实例化。而且只能用new来声明匿名内部类的定义

class PA {
   public void display() {
      System.out.println("在 A 类内部");
   }
}

class Demo {
   public void createClass() {

      // 创建的匿名类继承了 A 类
      A p1 = new A() {     //匿名内部类的声明
         public void display() {
            System.out.println("在匿名类内部");
         }
      };
      p1.display();
   }
}

class Main {
   public static void main(String[] args) {
       Demo an = new Demo();
       an.createClass();
   }
}

匿名内部类不能是抽象类。

匿名内部类不能定义构造器。由于匿名内部类没有类名,所以无法定义构造器,但匿名内部类可以初始化块,可以通过初始化块来完成构造器需要完成的工作。

10.4静态内部类

public class Test {
    public static void main(String[] args)  {
        Outter.Inner inner = new Outter.Inner();
    }
}
 
class Outter {
    int a=10;
    static int b=5;
    public Outter() {
         
    }
     
    static class Inner {  //静态内部类
        public Inner() {
             System.out.println(b);  //可以访问外部类的static成员,非static成员(如a)不可访问
            //外部类的非static成员必须依附于具体的对象
        }
    }
}

异常

异常的分类

  1. 检查性异常:用户的错误(或问题)而导致的异常,程序员无法预见这类异常。比如要打开一个不存在的文件时,会触发相关异常。
  2. 运行时异常:可能被程序员发现并避免。
  3. 错误(ERROR):错误不是异常,而是脱离程序员控制的问题。错误在代码中通常被忽略、例如,当栈溢出时,一个错误就发生了,他们在编译时也检查不到。

相关类

Java把异常当作对象来处理,并定义了一个积累java.lang.Throwable作为所有异常的超类。

在这里插入图片描述

Error

Error类对象由JVM生成并抛出,大多数错误与代码编写者所执行的操作无关。

JVM运行错误(Virtual MachineErroe),当JVM不再有继续执行操作所需的内存资源时,将出现OutOfMemoryError错误。这类错误发生时,JVM一般会选择终止线程。

还有发生在虚拟机试图执行应用时,如类定义错误(NoClassDefFoundError)、连接错误(LinkageError),这些错误不可查,因为他们在应用程序的控制和处理能力之外,而且绝大多数是程序运行时不允许出现的状况。

Exception

常见的运行时异常(RuntimeException)

异常名含义
ArrayIndexOutOfBoundException数组下标越界
NullPointerException空指针异常
ArithmeticException算数异常
MissingResourceException丢失资源
ClassNotFoundException找不到类

1.捕获和抛出异常

相关关键字:try、catch、finally、throw、throws

try、catch、finally

例:

public class demo{
    public static void main(){
        int a=1;
        int b=0;
        
        try{ //监控可能会出现异常的区域
            System.out.println(a/b);    // 1/0会出现ArithmeticException异常,后两句会直接跳过进入catch
            a++;
            b++;
        }
        catch(ArithmeticException e){   //若捕获到相关异常,就执行catch中的语句
            System.out.println("程序出现异常,b不能为0");
        }
        finally{   //finally中的语句一定会执行
            System.out.println("finally");
        }
    }
}

上述代码中,ArithmeticException是Exception的子类,如果catch中定义的是Throwable或Exception对象,则所有异常都可以捕获。异常捕获结束之后程序仍旧会运行。

catch可以写多个,如下

try{
	//......
}
catch(...){
	//......
}
catch(...){
	//......
}
..
finally{
	//......
}

不过catch中的异常范围必须逐步增大,比如第一个catch()中写了Exception对象,那么第二个catch()中就不能写ArithmeticException等类似的异常对象,因为他们是Exception的子类,范围更小。

IDEA中可以通过ctrl+alt+T快速创建if-else、try-catch等结构语句。

throw、throws

throw可以主动抛出异常,这里的异常是一个实例

int a=1,b=0;

if(b==0) throw new ArithmeticException(); //使用throw会提前报错结束线程

System.out.println(a/b);

throws用在方法中。主要是声明这个方法会抛出相关类型的异常,使它的调用者知道要捕获这个异常。throws不会像throw一样直接输出错误信息。

import java.util.*;

public class MainTest {
    public static void main(String[] args) {
        Scanner in=new Scanner(System.in);
        int a,b;
        while(in.hasNext()) {
            a = in.nextInt();
            b = in.nextInt();
            System.out.println(MainTest.test(a, b));
        }
        in.close();
    }

    public static int test(int a,int b) throws ArithmeticException{
        if(b==0){
            throw new ArithmeticException();
        }
        return a/b;
    }
}

效果

在这里插入图片描述

2.自定义异常

写一个类来继承Exception类即自定义异常

public class MyException extends Exception{
    private int detail;

    public MyException(int a){
        this.detail=a;
    }

    @Override
    public String toString(){
        return "MyException["+" detail="+detail+" ]";
    }
}

import java.util.*;

public class MainTest {
    public static void main(String[] args) {
        Scanner in=new Scanner(System.in);
        int a;
        while(in.hasNext()) {
            a = in.nextInt();
            try{
                test(a);
            }catch (MyException e){
                System.out.println("MyException-->"+e);
            }
        }
        in.close();
    }

    public static void test(int a) throws MyException{
        System.out.println("传入的参数为"+a);
        if(a>10){
            throw new MyException(a);
        }
        System.out.println("OK");
    }
}

效果

在这里插入图片描述

  • 处理运行时异常时,采用逻辑去合理规避的同时,辅助try-catch处理
  • 在多重catch快后面,可以加一个catch(Exception)来处理可能会被遗漏的异常
  • 对于不确定的代码,也可以加上try-catch,处理潜在的异常
  • 尽量去处理异常,切忌只是简单地调用printStackTrace()去打印输出
  • 具体如何处理异常,要更具不同的业务需求和异常类型去决定
  • 尽量添加finally语句块去释放占用的资源
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值