基础
1、Java语言发展史
- Java语言诞生于1995年。
- 其实在1995年之前SUN公司(太阳微电子公司:该公司目前被Oracle(甲骨文:做数据库的)收购了)为了占领智能电子消费产品市场,派James Gosling领导团队开发了一个Oak(橡树)语言。
- 1996年:JDK1.0诞生
- 什么是JDK?
JDK开发工具包
做Java开发必须安装的一个工具包,该工具包需要从官网下载。
目前SUN被Oracle收购了,所以下载需要去Oracle下载。
- 什么是JDK?
- Java包括三大块:
- JavaSE(Java标准版)
- JavaEE(Java企业版)
- JavaME(Java微型版)
其中JavaSE是基础,以后主攻方向是JavaSE方向。
2、Java语言特性【开源、免费、纯面向对象、跨平台】
- 简单性:
相对而言的,例如java中不再支持多继承,C++是支持多继承的,多继承比较复杂
C++中有指针,Java中屏蔽了指针的概念。
所以相对来说Java是简单的。
Java语言底层是C++实现的,不是C语言 - 面向对象
Java是纯面向对象的,更符合人的思维模式,更容易理解。 - 可移植性/跨平台
什么是可移植性?
java程序可以做到一次编译,到处运行。
也就是说java程序可以在windows操作系统上运行,不做任何修改,同样的java程序可以直接放到Linux操作系统上运行,这个被称为Java程序的可移植性,或者叫做跨平台。
windows操作系统内核和Linux操作系统的内核不同,两个操作系统执行指令的方式也是不一样的。java程序不直接和操作系统打交道,SUN的团队让java程序运行在一台虚拟的计算机当中,这个虚拟的计算机叫做Java虚拟机,简称JVM。JVM虚拟机再和底层的操作系统打交道,JVM有不同的版本。 - 多线程
- 健壮性:
和自动垃圾回收机制有关,自动垃圾回收机制简称GC机制。
Java语言运行过程中产生的垃圾是自动回收的,不需要程序员关心。 - 安全性
…
3、【重点】Java的加载与执行**
-
Java程序的运行包括两个重要的阶段,分别是:编译阶段和运行阶段
-
编译阶段
- 编译阶段主要的任务是检查Java源程序是否符合Java语法
- 字节码文件中不是纯粹的二进制,这种文件无法在操作系统当中直接执行。
- 编译生成的程序被称为xxxx.class字节码文件,一个java源文件可以编译生成多个.class文件
- 编译结束之后,可以将class文件拷贝到其他操作系统当中运行。【跨平台】
注意:设置【系统环境变量path】,加上jdk的bin目录,这样javac命令就可在任意目录下执行
-
运行阶段【可以在其他操作系统当中,跨平台】
-
JDK安装之后,除了自带一个javac.exe之外,还有另一个工具/命令,叫做java.exe,java.exe命令主要负责运行阶段。
-
java.exe在DOS窗口中使用,使用方法:java 类名
例如:
硬盘上有一个A.class,那么就这样用:java A(不能用成java A.class) -
运行阶段的过程是:
- 打开DOS命令窗口
- 输入:java A
- java.exe命令会启动Java虚拟机(JVM),JVM会启动类加载器ClassLoader
- ClassLoader会去硬盘上搜索A.class文件,找到该文件则将该字节码文件装载到JVM当中。
注意:类加载器默认从当前目录下寻找符合名字的class文件,如果没有将无法加载。可以设置【java环境变量classpath】(classpath环境变量属于java语言中的环境变量,不属于windows操作系统)来指定加载位置,这样你在其它目录使用java命令就会到指定的目录中寻找class文件。
- JVM将A.class字节码文件解释成二进制10101010这样数据
- 然后操作系统执行二进制和底层硬件平台进行交互。
4.Java 是编译与解释共存的语⾔。
-
Java在编译时期,通过将源代码编译成.class ,配合JVM这种跨平台的抽象,屏蔽了底层计算机操作系统和硬件的区别,实现了“一次编译,到处运行” 。 而在运行时期,目前主流的JVM 都是混合模式(-Xmixed),即解释运行 和编译运行配合使用。解释器的优势在于不用等待,编译器则在实际运行当中效率更高。在Java虚拟机运行时,解释器和即时编译器能够相互协作,各自取长补短,从而提高运行效率。
4、【重点】jdk与jre区别和联系
**1、JRE(Java Runtime Enviroment)**是Java的运行环境。面向Java程序的使用者,而不是开发者。如果你仅下载并安装了JRE,那么你的系统只能运行Java程序。JRE是运行Java程序所必须环境的集合,包含JVM标准实现及 Java核心类库。它包括Java虚拟机、Java平台核心类和支持文件。它不包含开发工具(编译器、调试器等)。安装jre的时候安装程序自动帮你把jre的 java.exe添加到了系统变量中
**2、JDK(Java Development Kit)**又称J2SDK(Java2 Software Development Kit),是Java开发工具包,它提供了Java的开发环境(提供了编译器javac等工具,用于将java文件编译为class文件)和运行环境(提 供了JVM和Runtime辅助包,用于解析class文件使其得到运行)。如果你下载并安装了JDK,那么你不仅可以开发Java程序,也同时拥有了运 行Java程序的平台。JDK是整个Java的核心,包括了Java运行环境(JRE),一堆Java工具tools.jar和Java标准类库 (rt.jar)。
注意:输入javac.exe与输入java -cp c:/jdk/lib/tools.jar com.sun.tools.javac.Main 是一样的,javac.exe只是一个包装器(Wrapper),而制作的目的是为了让开发者免于输入太长的指命。
3.最主流的JDK是Sun公司发布的JDK,除了Sun之外,还有很多公司和组织都开发了自己的JDK,例如IBM公司开发的JDK,BEA公司的Jrocket,还有GNU组织开发的JDK等等。
5、一个简单java文件
- 一个java源文件当中可以定义多个class(两种类型public class和class)
- 一个class会对应生成一个xxx.class字节码文件,一个java文件有几个class就生成几个class文件
- public class不是必须的,如果有的话只能有一个且名字与文件名一样
- 每一个class当中都可以编写main方法,都可以设定程序的入口,想执行B.class中的main方法:java B,
想执行X.class当中的main方法:java X
注意:当在命令窗口中执行java Hello,那么要求Hello.class当中必须有主方法。没有主方法会出现运行
阶段的错误:
6、标识符、变量与字面值
标识符
- 一个合法的标识符只能由“数字、字母、下划线、美元符号”组成,不能含有其他符号
- 不能以数字开头、严格区分大小写、关键字不能做标识符、理论上无长度限制,但是最好不要太长
变量
java变量共两类:
- 局部变量:在方法体当中声明的变量叫做局部变量
- 成员变量:在方法体之外(类体之内)声明的变量叫做成员变量,成员变量没有手动赋值系统会默认赋值(局部变量不会)
字面值
-
字面值含义:
- 10、100
- 3.14
- “abc”
- ‘a’
- true、false
字面值就是数据、字面值是java源程序的组成部分之一,包括标识符和关键字他们都是java源程序的组成部分
-
数据在现实世界当中是分门别类的,所以数据在计算机编程语言当中也有类型的:【数据类型】
- 10、100 属于整数型字面值
- 3.14 属于浮点型字面值
- true,false 属于布尔型字面值
- “abc”、“中国人” 属于字符串型字面值
- ‘A’、‘人’ 属于字符串型字面值
-
注意:
java语言当中所有的字符串型字面值必须使用双引号括起来
java语言当中所有的字符型字面值必须使用单引号括起来
7、数据类型
基本数据类型包括四大类八小种:
- 整数型
byte,short, int, long - 浮点型
float, double - 布尔型
boolean - 字符型
char
基本数据类型 | 占用空间大小(单位:字节) |
---|---|
byte | 1 |
short | 2 |
int | 4 |
long | 8 |
float | 4 |
double | 8 |
boolean | 1 |
char | 2 |
8、数据在计算机中的存储形式
- 为了方便计算机内部计算,数据在计算机内部都是以补码形式存储的
-
正数的补码、反码都是本身
-
负数的反码、补码
反码:符号位不变,其余位1变为0,0变为1。补码:反码+1
- 给出一个计算机内部存储的二进制数,需要转化为原码才能知道存储的是那一个数。1000 0001并不表示存储的是数字“ -1 ”,将其转化为原码为:减1取反:1111 1111即“ -127 ”。
- 对应的,给出一个数,计算机内部存储的是其补码,不能简单的对应二进制数,例如在强制类型转化时应该用补码考虑:
byte m = (byte)-129;
/**
*错误思考:直接使用对应二进制数:1000 0000 ... 1000 0001 ->(前3字节24位丢弃) -> 1000 0001(补码)->1111 1111(减1取反)=-127
*正确:计算机内部以补码形式存储:1111 1111 ... 0111 1111 ->(前3字节24位丢弃) -> 0111 1111(补码)->0111 1111(正数不变)=127
*所以最终强制转化后m=127
*/
8、运算符
逻辑运算符
- &和&&:当且仅当 a、b 都为 true 时,结果为 true ;
- | 和 || : 当且仅当 a、b 都为 false 时,结果为 false ;
- ^ : a 与 b 相异时,结果为 true ;
&、| 与 &&、|| 的区别:& 和 | 左右两边的式子一定会执行(比较笨),&& 和 || 只要左边的式子能得出结果,右边的式子就不会执行(比较聪明)。
位运算符
10、控制与循环
11、方法
方法内改变形参对实参的影响
1、对于基本类型参数,在方法体内对参数进行重新赋值,并不会改变原有变量的值。方法体内对参数进行运算,不影响原有变量的值。
2、对于引用类型参数,在方法体内对参数进行重新赋予引用,并不会改变原有变量所持有的引用。但是对参数所指向对象的属性进行操作,将改变原有变量所指向对象的属性值。
重载Overload
- 什么条件满足之后构成方法重载?
- 在同一个类中
- 方法名相同
- 参数列表不同:数量不同,顺序不同,类型不同
- 方法重载和什么有关系?和什么没有关系?
- 方法重载和方法名+参数列表有关系
- 方法重载和返回值类型无关
- 方法重载和修饰符列表无关
12、包机制
作用
方便项目管理、防止类名冲突
原理
- 使用了package后,类名就成为“包+class名”,同一包下可直接使用类名,不同包需要使用全类名或import导入后只使用类名。
- java.lang包下的所有类是默认导入的,所有我们平时的System、String等都没有使用全名和导入对应包,使用lang之外的包需要手动导入。
结论:什么时候使用import?不在同一个包且不在java.lang包下,这时需要用import导入。
13、访问权限修饰符
修饰类或接口(内部类除外)
public:所有
缺省:同一包下可见
修饰成员变量
- public:所有
- protect:同包下。子类。
- 缺省:同包下
- private:只有自己
修饰方法
与成员变量一样
注意:protect是专为子类提供的一种修饰符,在不同包时只有子类能访问。
面向对象
1、面向对象与面向过程的区别
- 面向对象因为有封装、继承、多态三大特性,所以使用面向对象开发出来的项目容易维护、代码耦合度低、易扩展和易复用,但是面向对象的性能一般比面向过程低,因为使用类时需要实例化、开销比较大。
- 面向过程不易维护、扩展和复用,但性能作为重要的考量因素时可以使用面向过程开发,比如单片机、嵌入式开发。当然性能高低不是这么简单就能说谁高谁低,只有在特定的场景下进行分析才是对的。
2、继承
继承的含义与作用
含义:继承是使⽤已存在的类的定义作为基础建⽴新类的技术,各个类之间的继承是“ is a ”关系,比如动物类与猫类,猫继承动物类,因为猫是一个动物。反之如果不满足is a的继承就不合理。
作用:代码复用,易扩展、维护,有了继承才有:方法覆盖和多态
继承的属性与方法
⼦类拥有⽗类对象所有的属性和⽅法(包括私有属性和私有⽅法),但是⽗类中的私有属性 和⽅法⼦类是⽆法访问,只是拥有。
成员变量的继承
-
对于父类的包访问权限成员变量,如果子类和父类在同一个包下,则子类能够继承,否则,子类不能够继承;
-
对于子类可以继承的父类成员变量,如果在子类中出现了同名称的成员变量,则会发生 隐藏 现象,即子类的成员变量会屏蔽掉父类的同名成员变量。如果要在子类中访问父类中同名成员变量,需要使用super关键字来进行引用。
成员方法的继承
-
对于父类的包访问权限成员方法,如果子类和父类在同一个包下,则子类能够继承,否则,子类不能够继承;
-
对于子类可以继承的父类成员方法,如果在子类中出现了同名称的成员方法,则称为 覆盖 ,即子类的成员方法会覆盖掉父类的同名成员方法。如果要在子类中访问父类中同名成员方法,需要使用super关键字来进行引用。
覆盖方法override
- ⽅法的重写要遵循“两同两⼩⼀⼤”(以下内容摘录⾃《疯狂 Java 讲义》,issue#892 ):
- “两同”即⽅法名相同、形参列表相同;
- “两⼩”指的是⼦类⽅法返回值类型应⽐⽗类⽅法返回值类型更⼩或相等,⼦类⽅法声明抛出 的异常类应⽐⽗类⽅法声明抛出的异常类更⼩或相等;
- “⼀⼤”指的是⼦类⽅法的访问权限应⽐⽗类⽅法的访问权限更⼤或相等。
注意:
- 如果⽅法的返 回类型是void和基本数据类型,则返回值重写时不可 修改。但是如果⽅法的返回值是引⽤类型, 重写时是可以返回该引⽤类型的⼦类的,即上面所说的小于等于。
- 构造方法无法重写
new一个子类时的构造过程
new一个新的子类对象时,会调用子类对应的构造方法,在子类的构造方法里会默认调用父类的无参构造方法,如果父类没有无参构造方法就会报错,这时就必须在子类构造方法里用super()去调用父类的其它有参构造方法,且必须放在第一行。
final关键字
-
final类:无法被继承,其方法默认都是final方法,属性不是final属性。
-
final属性:表示一个常量,无法被修改。final的成员变量需要定义时初始化赋值或在构造函数中赋值。一般final配合static声明类的静态常量:static final int PAI = 3.141;
-
final方法:表示这个方法无法被子类覆盖,不允许子类影响到该方法。
3、多态
含义
- 所谓多态就是指程序中定义的引⽤变量所指向的具体类型和通过该引⽤变量发出的⽅法调⽤在编 程时并不确定,⽽是在程序运⾏期间才确定,即⼀个引⽤变量到底会指向哪个类的实例对象,该 引⽤变量发出的⽅法调⽤到底是哪个类中实现的⽅法,必须在由程序运⾏期间才能决定。
- 在 Java 中有两种形式可以实现多态:继承(多个⼦类对同⼀⽅法的重写)和接⼝(实现接⼝并 覆盖接⼝中同⼀⽅法)。
特点
- 对象类型和引用类型之间具有继承(类)/实现(接口)的关系;
- 引用类型变量发出的方法调用的到底是哪个类中的方法,必须在程序运行期间才能确定;
- 多态不能调用“只在子类存在但在父类不存在”的方法,需要使用得考虑类型强制转化;
- 如果子类重写了父类的方法,真正执行的是子类覆盖的方法,如果子类没有覆盖父类的方法,执行的是父类的方法。
instanceof
语法:引用变量 instanceof 类
作用:判断该引用变量指向的类是否属于后面的类,是则返回true
4、抽象类
含义
-
在继承的层次中,位于最上层的类更具有一般性,一般人们是将它作为派生类的基类,而不是用来构造其特定的实例,这时候就可以将这个基类定义为抽象类。
-
在抽象类中由于其代表着一般性,有些方法是不需要实现出来,而是有其派生类具体实现,这时可以使用abstract修饰的抽象方法,抽象方法只需定义,不用实现,有抽象方法的类必须定义为抽象类,但抽象类与是否含有抽象方法没有关系。
-
抽象类的子类必须给出抽象类中的抽象方法的具体实现,除非该子类也是抽象类。
5、接口
语法
- 接口内只含有两部分内容:静态常量(public static final 可以省略)、抽象方法(public abstract可以省略)
- 一个类可以实现多个接口,接口可以继承多个接口
- 实现一个接口必须实现接口的所有方法,同时会继承该接口的所有静态常量。
接口与抽象类
- 一个类可以实现多个接口,一个类只能继承一个抽象类。
- 接口是完全抽象的,抽象类是半抽象的
- 抽象类与接口都不能实例化,但抽象类有构造方法用于初始化抽象类属性,而接口没有构造方法
- 接口是对行为的抽象和规范,抽象类是对类的抽象和规范
6、内部类
静态内部类
实例内部类
局部内部类
用得比较多的是局部内部类中的:匿名内部类(当你只想创建某类的一个对象时,就可以 new Type(){} )
一般使用情景:当方法形参需要一个实现某接口的类的对象时,为了方便就不再单独定义一个类实现这接口再new这个类的对象传给方法,而是直接在方法调用时使用匿名内部类。
//Arrays.sort(Object arr[], Comparator cmp),这里需要一个实现Conparator接口的对象,使其降序排序
Integer arr[] new Integer[]{1 ,2, 3};//使用Comparator排序数组,数组元素必须为引用类型,不能用int、double等基本数据类型
Arrays.sort(arr, new Comparator<Integer>(){
public int compare(Integer a, Integer b){
if(a > b) return -1;
if(a < b) return 1;
return 0;
}
})
7、类与类的关系
- is a:继承
- has a:关联(属性)
- like a:实现接口
8、泛型
分类
泛型类、泛型接口、泛型方法
一般语法
- 泛型标识:可以随便写任意标识号,标识指定的泛型的类型常见的如T、E、K、V等形式的参数常用于表示泛型
- 在使用泛型的时候如果传入泛型实参,则会根据传入的泛型实参做相应的限制,此时泛型才会起到本应起到的限制作用。如果不传入泛型类型实参的话,在泛型类中使用泛型的方法或成员变量定义的类型可以为任何的类型。
泛型通配符
Ingeter
是Number
的一个子类,在使用Generic<Number>
作为形参的方法中,不能使用Generic<Ingeter>
的实例传入。因此我们需要一个在逻辑上可以表示同时是Generic<Integer>
和Generic<Number>
父类的引用类型。由此类型通配符应运而生。
类型通配符一般是使用?代替具体的类型实参,注意了,此处’?’是类型实参,而不是类型形参 。再直白点的意思就是,此处的?和Number、String、Integer一样都是一种实际的类型,可以把?看成所有类型的父类。是一种真实的类型。
泛型类
略
泛型接口
//定义一个泛型接口
public interface Generator<T> {
public T next();
}
/**
* 未传入泛型实参时,与泛型类的定义相同,在声明类的时候,需将泛型的声明也一起加到类中
* 即:class FruitGenerator<T> implements Generator<T>{
* 如果不声明泛型,如:class FruitGenerator implements Generator<T>,编译器会报错:"Unknown class"
*/
class FruitGenerator<T> implements Generator<T>{
@Override
public T next() {
return null;
}
}
/**
* 传入泛型实参时:
*/
public class FruitGenerator implements Generator<String> {
private String[] fruits = new String[]{"Apple", "Banana", "Pear"};
@Override
public String next() {
Random rand = new Random();
return fruits[rand.nextInt(3)];
}
}
泛型方法
/**
* 泛型方法的基本介绍
* @param tClass 传入的泛型实参
* @return T 返回值为T类型
* 说明:
* 1)public 与 返回值中间<T>非常重要,可以理解为声明此方法为泛型方法。
* 2)只有声明了<T>的方法才是泛型方法,泛型类中的使用了泛型的成员方法并不是泛型方法。
* 3)<T>表明该方法将使用泛型类型T,此时才可以在方法中使用泛型类型T。
* 4)与泛型类的定义一样,此处T可以随便写为任意标识,常见的如T、E、K、V等形式的参数常用于表示泛型。
*/
public <T> T genericMethod(Class<T> tClass)throws InstantiationException ,
IllegalAccessException{
T instance = tClass.newInstance();
return instance;
}
对于一个static的方法而已,无法访问泛型类型的参数。所以如果static方法要使用泛型能力,就必须使其成为泛型方法。
泛型其它定义语法
1、泛型上下边界
为泛型添加上边界,即传入的类型实参必须是指定类型的子类型或本身:<? extends Number>
传入的类型实参必须是指定类型的父类型或本身:<? super T>
10、异常
Exception
和 Error
二者都是 Java 异常处理Throwable的重要子类,各自都包含大量子类。
Exception
:程序本身可以处理的异常,可以通过catch
来进行捕获。Exception
又可以分为 受检查异常(必须处理) 和 不受检查异常(可以不处理)。Error
:Error
属于程序无法处理的错误 ,我们没办法通过catch
来进行捕获 。例如,Java 虚拟机运行错误(Virtual MachineError
)、虚拟机内存不够错误(OutOfMemoryError
)、类定义错误(NoClassDefFoundError
)等 。这些异常发生时,Java 虚拟机(JVM)一般会选择线程终止。
除了RuntimeException
及其子类以外,其他的Exception
类及其子类都属于受检查异常 。常见的受检查异常有: IO 相关的异常、ClassNotFoundException
、SQLException
…。
异常处理方式
捕获异常:使用try-catch
方法抛出异常:使用throws关键字