Java知识实践—基础篇(一)

笔记

Posted by Jingming on August 11, 2018

本篇总结Java的编程环境和基础语法。

一、编程环境工具

JRE

JRE = JVM+ API

JRE运行程序时候的主要三项功能:

(1)class loader加载代码 (2)bytecode verifier校验代码 (3)runtime interpreter执行代码

JDK

JDK = JRE + Tools(工具包括编译器javac.exe,执行器java.exe,文档生成器javadoc.exe,打包jar.exe,调试器jdb.exe, 反汇编javap.exe等)

javap 类名:查看类信息

javap - c 类名:反汇编

Jar

jar cvfm A.jar A.man A.class

表示将A.man和A.class打包成A.jar, c表示创建,v表示创建时显示打包详情,f表示指定打包后的文件名,m表示指定了清单文件manifest(内容是class-path,main-class是哪个)。

运行时候使用A.jar:java -jar A.jar

Javadoc

javac -d 目录名 xxx.java可以将java文件中的@注释解析出来,生成一个html文档。

二、项目组织

Package

包层次的根目录是classpath决定的。

java编译器自动导入了java.lang.*

也可以使用javac -d 目录,来把编译后的.class放入该路径目录下。

常用的有java.util,java.io,java.net

包的理解

包可以理解为一个文件夹,该文件夹存在抽象的命名空间(也就是存在一种逻辑上的绝对路径)。包可以把同名类给区分开,类名称加上包名就可以在逻辑上定义一个相对于世界来说(也是Java解释器,因为世界都用标准的Java解释器)来说唯一的类。

实际中的包,也就是物理上的包是局部的。

理解:两个实际开发包A和B都可以为某个命名空间com.abc包贡献类,但是如果用户想使用命名空间com.abc包下的代码,那么就必须要引入具体的包。其中物理局部的是指AB包可以在不同的物理路径下。

PS.两个同名开发包里面定义了相同的类名如何理解:java解释器会有加载机制,对于普通包,都加入classpath的情况下,先加载到哪一个,就使用哪一个。也就是说,开发包A和B虽然都对命名空间com.abc包做了贡献,但是开发包A和B是有自己的绝对物理路径的。java解释器运行的时候,所有可以使用的类,就是classpath组合之后形成的树目录下的文件,显然,这种树目录和包的命名空间相对应。

三、变量和方法

引用型和基本型

引用可以看做是指针,字符串,数组,对象默认都传引用型。引用在栈上,但是可以修改堆里面指向的数据。

传参到底是按引用还是按值

答案就是看传进去的参数本身,如果参数本身是完全存在于栈上面的,例如基本类型且是临时变量,那么就是按值的,也就是复制一份;如果参数本身是部分存在于栈上面的,例如String对象,那么栈上面其实是引用,此时传入的是引用的副本,通过该引用完全可以修改之前的String对象,造成按引用传递的效果。如果引用的副本变化,例如指向其他的String对象,并不会对之前String对象造成影响。

参考:https://www.zhihu.com/question/31203609

四、类和接口

继承

(1)子类extends父类所有字段,如果子类有自己相同名的变量,那么覆盖父类定义。父类的可以使用super来调用,也就是说并不是物理覆盖。super相当于父类对象的this引用。

(2)父类所有私有方法被子类自动继承。如果子类有同名方法,那么也是覆盖,也就是override。

(3)父类的构造方法是不能被继承的,子类的构造方法的第一句可以使用super来调用父类的构造方法。

访问控制

默认是包访问。包访问介于protected和private之间。

类的访问控制符,也就是说不写public的情况下,只有包内部才能访问该类。

一个文件只能有一个public类,且只能与文件名相同。

static,final,private

static意思就是修饰的类、属性只属于类的,不属于实例,final指的是不能被修改(只读) 。修饰类、方法、变量具体含义不同 。

static类型初始化时机不确定,第一次使用类时候初始化,但先于实例的初始化。

private的意思就是不能通过对象直接访问,也就是说,被修饰类、变量只能对内部可见。

private static属性:就是类内部才能访问的,且只能由static方法来访问。

接口

接口里的方法都是默认public abstract的,属性都是默认public,static,final的。

接口的理解:例如集合的接口里面就是增删查,大小。具体集合具体实现方式不一样。

构造方法

(1)默认构造方法只有没有其他构造方法时候,才会自动添加。

(2)构造方法里面不能调用虚方法,因为虚方法会调用子类,而子类并没有构造好。如果要调用方法,那么最好调用final的方法。

析构函数:finalize()

调用时机是垃圾回收对象的时候,先释放子类资源,在一层层的释放父类资源。

内部类

(1)内部类编译后是:外部类名$内部类名.class。 (2)类外使用内部类,也就是创建内部类对象:外部类.内部类 变量 = 外部对象名.new 内部类名(参数)。 (3)内部类直接访问外部类字段和方法,包括private。如果同名,要使用外部类名.this.变量名 (4)内部类可以被public、protected、private所修饰。如果被static所修饰,那其实不是内部类了,可以称为嵌套类,且他不能访问外部类的非static字段和方法,因为内部static类不依赖外部类的实例。

局部类

方法中定义的类称为局部类。局部类相当于局部变量,所以不能public,private,static所修饰,但是可以被final和abstract所修饰。

可以访问外部类的成员变量,但不能访问外部方法的非final局部变量。

匿名类

匿名类就是一次性使用的类。定义同时就生成一个实例,使用完之后消失,不需要关心叫什么名字。匿名类是内部类的简写。 应用:

(1)事件处理,传入匿名类,匿名类中重写了处理方法。

(2)排序时候的比较器(c++中的functor)。

五、Java一些机制

Collections Checked vs Generics

泛型只能编译期静态的检查是不是不合理,而Collections Checked可以动态的检测集合中的类型是不是合法的类型。

垃圾回收

引用计数为0(因为有些引用被赋予null)的时候,回收。调用System.gc(),但是该函数不一定立即执行。 垃圾回收的GC Roots:如果从这些root可以到达的对象,被认为是活着的对象,把他们挪到新空间,之前空间就可以直接回收。 有:(1)java方法栈中正在引用的对象;(2)方法区中类引用对象;(3)方法区中常量引用对象;(4)JNI方法栈中引用的对象。 引用计数法:对象会记录自己引用的对象,自己消失的时候,在引用计数表里面先把自己依赖的对象对应的引用数减1。

缺点:无法解决循环引用(只要引用形成闭合环),例如对象A和对象B相互依赖,这样如果要回收A,那么需要先回收B,否则A总有B引用它,反之亦然,所以矛盾。 标记清除法:把对象标记为存活与否,缺点是大量碎片。

复制收集:内存分为两半,存活的对象复制到另一块上,然后清除之前一块。缺点:浪费空间。解决方法:将对象分为新老永三代。新生代是非常容易被回收的对象,老年代的对象是内存中存活时间很长的对象。新生代分配空间时候分为三块,一块较大E,另两块较小的From,To,每次使用E空间,E第一次满了的时候,会把存活对象移到From里面,这些活着的对象就1岁了。第二次E满的时候,那么这次将E+From中的活着对象,然后移动到To(To中有一岁和两岁的对象),最后清空E和From,并将To和From进行交换。现在知道了,分为三块的目的就是要统计对象的年龄。当对象年龄达到一定的阈值后,那么就直接放入老年代内存,老年代内存不够时候会执行gc进行标记清除、整理压缩。老年代会直接放入对垃圾回收没有显著影响的对象,

反射机制

用处之一:根据配置文件来生成对象或者调用具体方法,那么就可以先拼接字符串成方法名,然后调用即可。如果没有反射机制,可以想象已有对象结合某些字符串,我们是不知道如何具体调用他那个方法的。