• java基础巩固2


    抽象类

    (1)如果父类的方法本身不需要实现任何功能,仅仅是为了定义方法签名,目的是让子类去覆写它,那么就可以把父类的方法声明为抽象方法。
    (2)把一个方法声明为abstract,表明它是一个抽象方法,本身没有实现任何方法语句。因为这个抽象方法本身是无法执行的,所以父类也无法被实例化。因此,必须把父类也声明为abstract,才能正确编译它。
    (3)因为抽象类本身被设计成只能用于被继承,因此,抽象类可以强迫子类实现其定义的抽象方法,否则会编译报错。因此,抽象方法实际相当于定义了规范。
    (4)当我们定义了抽象类父类,以及具体的子类的时候,我们可以通过抽象类父类型去引用引用具体的子类的实例。这种引用抽象类的好处在于,我们对其进行方法调用,并不关心Person类型变量的具体子类型。这种尽量引用高层类型,避免引用实际子类型的方式,称之为面向抽象编程。
    面向抽象编程的本质就是:
    一、上层代码只定义规范;
    二、不需要子类就可以实现业务逻辑(正常编译);
    三、具体的业务逻辑由不同的子类实现,调用者不用关心。

    接口

    (1)在抽象类中,抽象方法本质上是定义接口规范:即定义高层类的接口,从而保证所有子类都有相同的接口实现,这样,多态就能发挥出威力。
    (2)如果一个抽象类没有字段,所有方法全都是抽象方法,就可以把抽象类改为接口:interface。所谓interface,就是比抽象类还要抽象的纯抽象接口,因为它连字段都不能有。因为接口定义的所有默认方法都是public abstract的,所以这两个修饰符不需要写出来。
    (3)当一个具体的class去实现一个interface时,需要使用implements关键字。在java中,一个类只能继承自另一个类,不能从多个类继承。但是,一个类可以实现多个interface。
    (4)一个接口可以继承自另一个接口。interface继承自interface使用extends,相当于扩展了接口的方法。
    (5)在接口中,可以定义default方法。实现类可以不必覆写default方法。default方法的目的是,当我们需要给接口新增一个方法时,会涉及到修改全部子类。如果新增的是default方法,那么子类就不必全部修改,只需要在需要覆写的地方去覆写新增方法。default方法和抽象类的普通方法有所不同。因为interface没有字段,default无法访问字段,而抽象类普通方法可以访问实例字段。

    静态字段和静态方法

    (1)在一个class中定义的字段,我们称之为实例字段。实例字段的特点是,每个实例都有独自的字段,各个实例的同名字段互不影响。还有一种字段,使用static修饰的字段,称为静态字段:static field、实例字段在每个实例中都有自己的一个独立空间,但是静态字段只有一个共享空间,所有实例都会共享该字段。对于静态字段,无论修改哪个实例的静态字段,效果都是一样的:所有实例的静态字段都被修改了,原因是静态字段并不属于实例。
    (2)因此并不推荐使用实例变量.静态字段去访问静态字段,因为在java程序中,实例对象并没有静态字段。在代码中,实例对象能访问静态字段只是因为编译器可以根据实例类型自动转化为类名.静态字段来访问静态对象。推荐使用类名.静态字段来访问静态对象。
    (3)调用实例方法必须通过一个实例变量,而调用静态方法则不需要实例变量,通过类名就可以调用。静态方法类似于其他编程语言的函数。因为静态方法属于class而不属于实例,因此,静态方法内部,无法访问this变量,也无法访问实例字段,它只能访问静态字段。通过实例变量也可以调用静态方法,但这只是编译器自动帮我们实例改成类名而已。通常情况下,通过实例变量访问静态字段和静态方法,会得到一个编译警告。静态方法经常用于工具类,例如:Arrays.sort(),Math.random()。静态方法也经常用于辅助方法。注意到Java程序的入口main()也是静态方法。
    (4)因为interface是一个纯抽象类,所以它不能定义实例字段。但是,interface是可以有静态字段的,并且静态字段必须为final类型。实际上,因为interface的字段只能是public static final类型,所以我们可以把这些修饰符都去掉。编译器会自动把字段变为public static final类型。

    作用域

    (1)protected作用域继承关系。定义为protected的字段和方法可以被子类访问,以及子类的子类。
    (2)包作用域是指一个类允许访问同一个package的没有public、private修饰的class,以及没有public、protected、private修饰的字段和方法。只要在同一个包,就可以访问package权限的class、field和method。注意,包名必须完全一致,包没有父子关系,com.apache和com.apache.abc是不同的包。

    局部变量

    (1)在方法内部定义的变量称之为局部变量,局部变量作用域从变量声明处开始到对应的块结束。方法参数也是局部变量。
    (2)java还提供了一个final修饰符。final域访问权限不冲突,它有很多作用。用final修饰class可以阻止被继承,用final修饰method可以阻止被子类覆写,用final修饰field可以阻止被重新赋值。
    (3)如果不确定是否需要public,就不声明为public,即尽可能少地暴露对外的字段和方法。把方法定义package权限有助于测试,因为测试类和被测试类只要位于同一个package,测试代码就可以访问被测试类的package权限方法。一个.java文件只能包含一个public类,但可以包含多个非public类。如果有public类,文件名必须和public类的名字相同。

    内部类

    (1)内部类的实例不能单独存在,必须依附于一个外部类的实例。内部类和普通类相比,除了能引用外部类实例外,还有一个额外的特权,就是可以修改外部类的private字段,因为内部类的作用域在外部类内部,所以能访问外部类的private字段和方法。观察Java编译器编译后的.class文件可以发现,Outer类被编译Outer.class,而Inner类被编译为Outer$Inner.class。
    (2)还有一种定义Inner Class的方法,它不需要在Outer Class中明确地定义这个class,而是在方法内部,通过匿名类(Anonmous Class)来定义。匿名类和Inner Class一样,可以访问Outer Class的private字段和方法。之所以我们要定义匿名类,是因为在这里我们通常不关心类名,比直接定义Inner Class可以少些很多代码。观察Java编译器编译后的.class可以发现,Outer类被编译为Ouer.class,而匿名类被编译为Outer$1.class。如果有多个匿名类,Java编译器会将每个匿名类依次命名为Outer$1、Outer$1、Outer$3

    classpath和jar

    (1)classpath是JVM用到的一个环境变量,它用来指示JVM如何搜索class。因为Java是编译型语言,源码文件是.java,而编译后的.class文件才是真正可以被JVM执行的字节码。因为,JVM需要知道,如果加载一个abc.xyz.Hello的类,应该去哪搜索对应的Hello.class文件。所以,classpath就是一组目录的集合,它设置的搜索路径与操作系统相关。例如,在Windows系统上,用;分割,带空格的目录用""括起来。
    (2)classpath的设定方法有两种:一、在系统环境变量中设置classpath环境变量,不推荐;二:在启动jvm时设置classpath变量,推荐。不需要把任何java核心库添加到classpath中,jvm根本不依赖classpath加载核心库。
    (3)如果有很多.class文件,散落在各层目录中,肯定不便于管理。jar包可以把package组织的目录层级,以及各个目录下的所有文件(包括.class文件和其他文件)都打成一个jar文件,这样一来,无论是备份还是发送客户都会更加方便。jar包实际上就是一个zip格式的压缩文件,而jar包相当于目录。如果我们要执行一个jar包的class,就可以把jar包放到classpath中。

    模块

    (1)从java9开始,jdk引入了模块(Module)。我们知道,.class文件是jvm看到的最小可执行文件,而一个大型程序需要编写很多Class,并生成一堆.class文件,很不便于管理,所以,jar文件就是class文件的容器。在java9之前,一个大型java程序会生成自己的jar文件,同时引用依赖第三方jar文件,而jvm自带的Java标准库,实际上也是以jar文件形式存放的,这个文件叫rt.jar。如果漏写了某个运行时需要的jar,那么运行时很有可能抛出ClassNotFoundException。所以,jar只是用于存放class的容器,它并不关心class之间的依赖。
    (2)模块主要就是为了解决依赖这个问题。如果a.jar必须依赖于另一个b.jar才能运行,那我们应该给a.jar加点说明,让程序编译和运行的时候能自动定位到b.jar,这种自带依赖关系的class容器就是模块。把一堆class封装为jar仅仅是一个打包的过程,而把一堆class封装为模块则不但需要打包,还需要写入依赖关系,并且还可以包含二进制代码。此外。模块支持多版本,即在同一个模块中可以为不同的jvm提供不同的版本。

    字符串

    (1)java字符串是不可变对象
    (2)字符串操作不改变原字符串内容,而是返回新字符串。
    (3)java使用unicode编码表示String和char。
    (4)转换编码就是将String和byte[]转换,需要指定编码。
    (5)转换byte[]时,始终优先考虑UTF-8编码。

    StringBuilder

    (1)java虽然可以拼接字符串,但是,在循环中,每次循环都会创建新的字符串对象,然后扔掉旧的字符串。这样,绝大多数字符串都是临时对象,不但浪费内存,还会影响GC效率。为了能高效拼接字符串,java标准库提供了StringBuilder,它是一个可变对象,可以预分配缓冲区,这样往StringBuilder中新增字符时,不会创建新的临时对象;
    (2)要高效拼接字符串,应该使用StringBuilder。String还提供了一个静态方法join(),这个方法在内部使用了StringJoiner来拼接字符串,在不需要制定开头和结尾的时候,使用String.join()更方便。

    包装类型

    (1)我们知道,java的数据类型分为两种:
    一、基本类型:byte,short,int,long,boolean,float,double,char
    二、引用类型:所有class和interface类型
    引用类型可以赋值为null,表示空,但基本类型不能赋值为null。那要把一个基本类型视为对象(引用类型),则需要定义包装类。因为int和Integer可以互相转换,这种直接把int变为Integer的赋值写法,称为自动装箱(Auto Boxing),反过来,把Integer变为int的赋值写法,称为自动拆箱(Auto Unboxing)。自动装箱和拆箱只发生在编译阶段,目的是为了减少代码。装箱和拆箱会影响代码的执行效率,因为编译后的class代码是严格区分基本类型和引用类型的。并且,自动拆箱的时候可能会报NullPointerException。
    (2)所有的包装类型都是不变类。对于两个Integer实例进行比较要特别注意:绝对不能用==比较,因为Integer是引用类型,必须使用equals比较。
    (3)整数和浮点数的包装类型都继承自Number。

  • 相关阅读:
    “Alibaba Druid 未授权访问” 安全漏洞
    【Redis】.net core Redis事件订阅与发布,基础篇
    深入理解多线程(第三篇)
    汽车油封的重要性
    Linux中常用的性能分析工具
    vant_vant引入
    【机试题】两个链表相减,并以相同形式返回一个表示相减结果的链表
    React 学习笔记:JSX 语法
    基于LVM通过添加硬盘实现分区扩容的方法介绍
    SpringMVC入门宝典(八)SpringMVC异常处理
  • 原文地址:https://blog.csdn.net/weixin_49131718/article/details/125977352