从C++到一点点Scala,又学了一点Python,再回到Java。
互联网时代,Java基础还是得打打牢。从java web到大数据,都离不开它。
学习廖雪峰的课程,速刷一遍。每天更新一篇笔记。
运行java程序执行java classname
比如有Hello.class,执行java Hello即可;若执行java Hello.class则会报错。
Java 中的length 、length()、size()
可变参数
详见:https://www.liaoxuefeng.com/wiki/43744/08320
变量没有冲突时this可省略
class前修饰符,加public和不加public的区别
加public表示全局类,该类可以import到任何类内。
不加public默认为保留类,只能被同一个包内的其他类引用。
继承
用extends关键字:
子类自动获得了父类的所有字段,严禁定义与父类重名的字段!
Java只允许一个class继承自一个类(你只有一个亲爹!),一个父类可以有多个子类。默认继承自object类,只有Object没有父类。
protected关键字,允许子类访问父类的字段。
super()
如果父类没有默认的构造方法,子类就必须显式调用super()并给出参数以便让编译器定位到父类的一个合适的构造方法。
这里还顺带引出了另一个问题:即子类不会继承任何父类的构造方法。子类默认的构造方法是编译器自动生成的,不是继承的。
向上转型(upcasting)
子类变父类,完全ok,因为子类肯定有父类所有功能:
向下转型(downcasting)
父类变子类,失败;子类向上转成的父类,再转回子类,则OK!(反复横跳,结果挺好)
在向下转型的时候,把p1转型为Student会成功,因为p1确实指向Student实例,把p2转型为Student会失败,因为p2的实际类型是Person,不能把父类变为子类,因为子类功能比父类多,多的功能无法凭空变出来。
为了避免向下转型出错,可以用操作符:
多态 Polymorphic
多态的特性就是,运行时才能动态决定调用的子类方法。对某个类型调用某个方法,执行的实际方法可能是某个子类的覆写方法。
利用多态,totalTax()方法只需要和Income打交道,它完全不需要知道Salary和StateCouncilSpecialAllowance的存在,就可以正确计算出总的税。
多态具有一个非常强大的功能,就是允许添加更多类型的子类实现功能扩展,却不需要修改基于父类的代码。
final
可用于类或方法前,表示禁止继承!即不能被override。
也可用于字段前,表示禁止修改。
抽象类
如果父类的方法本身不需要实现任何功能,仅仅是为了定义方法签名,目的是让子类去覆写它,那么,可以把父类的方法声明为抽象方法,然后必须把Person类本身也声明为abstract,才能正确编译它:
抽象类本身被设计成只能用于被继承,因此,抽象类可以强迫子类实现其定义的抽象方法,否则编译会报错。因此,抽象方法实际上相当于定义了“规范”。
面向抽象编程的本质就是:
- 上层代码只定义规范(例如:abstract class Person);
- 不需要子类就可以实现业务逻辑(正常编译); java入门零基础老师
- 具体的业务逻辑由不同的子类实现,调用者并不关心。
总是拿抽象类调方法即可。
接口
如果一个抽象类没有字段,所有方法全部都是抽象方法:
就可以把该抽象类改写为接口::
public abstract这两个修饰符不需要写出来(写不写效果都一样)。
当一个具体的class去实现一个interface时,需要使用implements关键字。举个例子:
在Java中,一个类只能继承自另一个类,不能从多个类继承。但是,一个类可以实现多个interface,例如:
抽象类和接口对比:
接口可以继承另一个接口,使用:
接口可以有方法:
default方法和抽象类的普通方法是有所不同的。因为interface没有字段,default方法无法访问字段,而抽象类的普通方法可以访问实例字段。
静态字段和静态方法
静态字段
用static修饰的字段,称为静态字段:static field。
它们不属于某个实例,而是属于这个类的!
实例字段在每个实例中都有自己的一个独立“空间”,但是静态字段只有一个共享“空间”,所有实例都会共享该字段:
不推荐用实例变量.静态字段去访问静态字段,因为在Java程序中,实例对象并没有静态字段。在代码中,实例对象能访问静态字段只是因为编译器可以根据实例类型自动转换为类名.静态字段来访问静态对象。
推荐用类名来访问静态字段。可以把静态字段理解为描述class本身的字段(非实例字段)。对于上面的代码,更好的写法是:
静态方法
同样直接可以拿类名去调用,不用实例化。
静态方法内部,无法访问this变量,也无法访问实例字段,它只能访问静态字段。因为静态方法属于class而不属于实例。
静态的属性和方法在程序启动的时候,就全部装入内存的,而不管这些方法、属性以后有没有用到。
静态方法经常用于工具类。例如:
静态方法也经常用于辅助方法。注意到Java程序的入口也是静态方法。
练习
给Person类增加一个静态字段count和静态方法getCount,统计实例创建的个数。
打印:
接口静态字段
interface虽热不能有实例字段,但可以有静态字段,并且静态字段必须为final类型:
可以把去掉,编译器默认就加上了:
接口静态字段必须给默认值。
包
在Java虚拟机执行的时候,JVM只看完整类名,因此,只要包名不同,类就不同。
要特别注意:包没有父子关系。java.util和java.util.zip是不同的包,两者没有任何继承关系。
编译后的.class文件也需要按照包结构存放:
位于同一个包的类,可以访问包作用域的字段和方法。不用public、protected、private修饰的字段和方法就是包作用域。
导入包的方法:
- 用全称:
- 用: 然后写即可;
- 可以,但不推荐,因为导入包太多后,就分不清类到底属于哪个了;
- ,导入一个类的静态字段和静态方法,,即导入了System类的所有静态字段和静态方法;
Java编译器检查类的顺序
Java编译器最终编译出的.class文件只使用完整类名,因此,在代码中,当编译器遇到一个class名称时:
如果是完整类名,就直接根据完整类名查找这个class;
如果是简单类名,按下面的顺序依次查找:
- 查找当前package是否存在这个class;
- 查找import的包是否包含这个class;
- 查找java.lang包是否包含这个class。
如果按照上面的规则还无法确定类名,则编译报错。
编译器默认的两个import
- 默认自动import当前package的其他class;
- 默认自动import java.lang.*。
**实践
- 推荐的做法是使用倒置的域名来确保唯一性,如:
- 要注意不要和java.lang包的类重名,即自己的类不要使用这些名字:
String
System
Runtime
…
也不要和JDK常用类重名:
java.util.List
java.text.Format
java.math.BigInteger
…
作用域
Java内建的访问权限包括public、protected、private和package权限;
包作用域
包作用域是指一个类允许访问同一个package的没有public、private修饰的class,以及没有public、protected、private修饰的字段和方法。
final
- final class,防止类被继承;
- protected final void hi(),防止方法被覆写;
- private final int n = 0, 防止字段被重新赋值;
- final int t 参数,防止局部变量t被重新赋值;
嵌套类
定义在一个class内部的class称为嵌套类(nested class),嵌套类拥有访问private的权限。
**实践
- 如果不确定是否需要public,就不声明为public,即尽可能少地暴露对外的字段和方法。
- 把方法定义为package权限有助于测试,因为测试类和被测试类只要位于同一个package,测试代码就可以访问被测试类的package权限方法。
- 一个.java文件只能包含一个public类,但可以包含多个非public类。如果有public类,文件名必须和public类的名字相同。
内部类Inner Class
普通内部类 Inner Class
要实例化一个Inner,我们必须首先创建一个Outer的实例,然后,调用Outer实例的new来创建Inner实例:
因为Inner Class除了有一个this指向它自己,还隐含地持有一个Outer Class实例,可以用Outer.this访问这个实例。所以,实例化一个Inner Class不能脱离Outer实例。
Inner Class和普通Class相比,除了能引用Outer实例外,还有一个额外的“特权”,就是可以修改Outer Class的private字段,因为Inner Class的作用域在Outer Class内部,所以能访问Outer Class的private字段和方法。
观察Java编译器编译后的.class文件可以发现,Outer类被编译为Outer.class,而Inner类被编译为Outer$Inner.class。
匿名类 Anonymous Class
以上的代码创建了一个匿名类对象 object1,匿名类是表达式形式定义的,所以末尾以分号 ; 来结束。
通常继承一个父类或实现一个接口:
到这有点懂了,这有点像lambda表达式,不关心名称,只关心结果。
静态内部类 Static Nested Class
最后一种内部类。
用static修饰的内部类和Inner Class有很大的不同,它不再依附于Outer的实例,而是一个完全独立的类,因此无法引用Outer.this,但它可以访问Outer的private静态字段和静态方法。如果把StaticNested移到Outer之外,就失去了访问private的权限。
内部类小结
Java的内部类可分为Inner Class、Anonymous Class和Static Nested Class三种:
Inner Class和Anonymous Class本质上是相同的,都必须依附于Outer Class的实例,即隐含地持有Outer.this实例,并拥有Outer Class的private访问权限;
Static Nested Class是独立类,但拥有Outer Class的private访问权限。
classpath
classpath是JVM用到的一个环境变量,它用来指示JVM如何搜索class。
classpath的设定方法有三种:
- 在系统环境变量中设置classpath环境变量,不推荐;
- 在启动JVM时设置classpath变量,推荐:
java -classpath .;C:workproject1bin;C:shared abc.xyz.Hello
或者使用-cp的简写:
java -cp .;C:workproject1bin;C:shared abc.xyz.Hello - 不用前两种方法,默认为,即当前目录。默认的当前目录.对于绝大多数情况都够用了。
在IDE中运行Java程序,IDE自动传入的-cp参数是当前工程的bin目录和引入的jar包。
jar包 Java Archive
jar包可以把package组织的目录层级,以及各个目录下的所有文件(包括.class文件和其他文件)都打成一个jar文件,这样一来,无论是备份,还是发给客户,就简单多了。
jar包相当于一个目录。如果我们要执行一个jar包的class,就可以把jar包放到classpath中:
这样JVM会自动在hello.jar文件里去搜索某个类。
jar包实际上就是一个zip格式的压缩文件。可以手动把一组类打包成zip文件,再把后缀改为.jar,一个jar包就创建成功。
META-INF
大多数 JAR 文件包含一个 META-INF 目录,它用于存储包和扩展的配置数据,如安全性和版本信息。Java 2 平台(标准版【J2SE】)识别并解释 META-INF 目录中的下述文件和目录,以便配置应用程序、扩展和类装载器:
- MANIFEST.MF:可以指定Main-Class和其它信息。JVM会自动读取MANIFEST.MF文件。如果存在Main-Class,就可不必在命令行指定启动的类名,而是用更方便的命令:
jar包还可以包含其它jar包,这个时候,就需要在MANIFEST.MF文件里配置classpath了。
在大型项目中,不可能手动编写MANIFEST.MF文件,再手动创建zip包。Java社区提供了大量的开源构建工具,例如Maven,可以非常方便地创建jar包。 - 通过 MAVEN 插件打包进来的文件比如:
- 还有一些不常看到的:
参考:聊一聊 JAR 文件和 MANIFEST.MF
模块 Module
jar只是用于存放class的容器,它并不关心class之间的依赖。
从Java 9开始引入的模块,主要是为了“依赖”这个问题。如果a.jar必须依赖另一个b.jar才能运行,那我们应该给a.jar加点说明啥的,让程序在编译和运行的时候能自动定位到b.jar,这种自带“依赖关系”的class容器就是模块。
为了表明Java模块化的决心,从Java 9开始,原有的Java标准库已经由一个单一巨大的rt.jar分拆成了几十个模块,这些模块以.jmod扩展名标识,可以在目录下找到它们:
这些.jmod文件每一个都是一个模块,模块名就是文件名。例如:模块java.base对应的文件就是java.base.jmod。模块之间的依赖关系已经被写入到模块内的module-info.class文件了。所有的模块都直接或间接地依赖java.base模块,只有java.base模块不依赖任何模块,它可以被看作是“根模块”,好比所有的类都是从Object直接或间接继承而来。
版权声明:
本文来源网络,所有图片文章版权属于原作者,如有侵权,联系删除。
本文网址:https://www.bianchenghao6.com/h6javajc/20345.html