一 Java概述
Java可以干什么? Java主流是做web服务端开发,有着spring这个强大的框架支撑,也可以写Android,也在大数据领域有着重要的贡献,其中Kafka,Spark等框架有和Java有关联。
1.1 Java叙述
1、Java是SUN(Stanford University Network,斯坦福大学网络公司)1995年推出的一门高级编程语言。
2、Java是一种面向Internet的编程语言。Java一开始富有吸引力是因为Java程序可以在Web浏览器中运行。这些Java程序被称为Java小程序(applet)。applet使用现代的图形用户界面与Web用户进行交互。applet内嵌在HTML代码中。现在applet已经逐渐被淘汰了。
3、Java随着Java技术在web方面的不断成熟,已经成为Web应用程序的首选开发语言。
1.2 Java历史
时间 | 历史进程 |
---|---|
1991年 | Green项目,开发语言最初命名为Oak (橡树) |
1994年 | 开发组意识到Oak 非常适合于互联网 |
1996年 | 发布JDK 1.0,约8.3万个网页应用Java技术来制作 |
1997年 | 发布JDK 1.1,JavaOne会议召开,创当时全球同类会议规模之最 |
1998年 | 发布JDK 1.2,同年发布企业平台J2EE |
1999年 | Java分成J2SE、J2EE和J2ME,JSP/Servlet技术诞生 |
2004年 | 发布里程碑式版本:JDK 1.5,为突出此版本的重要性,更名为JDK 5.0 |
2005年 | J2SE -> JavaSE,J2EE -> JavaEE,J2ME -> JavaME |
2009年 | Oracle公司收购SUN,交易价格74亿美元 |
2011年 | 发布JDK 7.0 |
2014年 | 发布JDK 8.0,是继JDK 5.0以来变化最大的版本 |
2017年 | 发布JDK 9.0,最大限度实现模块化 |
2018年3月 | 发布JDK 10.0,版本号也称为18.3 |
2018年9月 | 发布JDK 11.0,版本号也称为18.9 |
2021年9月 | 发布JDK17 |
1.3 主要特性
1)Java语言是安全的。Java通常被用在网络环境中,为此,Java提供了一个安全机制以防恶意代码的攻击。如:安全防范机制(类ClassLoader),如分配不同的名字空间以防替代本地的同名类、字节代码检查。
2)Java语言是体系结构中立的。Java程序(后缀为java的文件)在Java平台上被编译为体系结构中立的字节码格式(后缀为class的文件),然后可以在实现这个Java平台的任何系统中运行。
3)Java语言是解释型的。如前所述,Java程序在Java平台上被编译为字节码格式,然后可以在实现这个Java平台的任何系统的解释器中运行。
4)Java是性能略高的。与那些解释型的高级脚本语言相比,Java的性能还是较优的。
5)Java语言是原生支持多线程的。在Java语言中,线程是一种特殊的对象,它必须由Thread类或其子(孙)类来创建。
1.4 Java的两种核心机制
1.4.1 Java虚拟机
- JVM是一个虚拟的计算机,具有指令集并使用不同的存储区域。负责执行指令,管理数据、内存、寄存器。
- 对于不同的平台,有不同的虚拟机。
- 只有某平台提供了对应的java虚拟机,java程序才可在此平台运行。
- Java虚拟机机制屏蔽了底层运行平台的差别,实现了“一次编译,到处运行”。
1.4.2 垃圾回收
- 不再使用的内存空间应回收—— 垃圾回收。
- 在C/C++等语言中,由程序员负责回收无用内存。
- Java 语言消除了程序员回收无用内存空间的责任:它提供一种系统级线程跟踪存储空间的分配情况。并在JVM空闲时,检查并释放那些可被释放的存储空间。
- 垃圾回收在Java程序运行过程中自动进行,程序员无法精确控制和干预。
- Java程序还会出现内存泄漏和内存溢出问题吗?Yes!
二 基础语法
八种基本数据类型 ①byte ②short ③int ④long ⑤float ⑥double ⑦char ⑧boolean
2.1 switch可以接的数据类型
1)byte,short,int,char
2)枚举类(JDK1.5新增)
3)String(JDK1.7新增)
2.2 数组的排序
概念:目的是快速找到数组中某个元素衡量排序算法的优势。
时间复杂度:分析关键字的比较次数和记录的移动的次数。
空间复杂度:分析排序算法需要多少辅助内存。
稳定性:若是两个记录A和B的关键字值相等,但是排序后A和B的先后顺序不变,则表示这种算法是稳定的。
2.2.1 排序算法
具体实现见:算法篇
1)冒泡排序
2)归并排序
3)快速排序
4)选择排序
2.2.2 数据工具类
Arrays
1)常用方法
// 比较两个数组的元素是否依次相等
boolean euqals(int[] a, int[] b)
// 输出数组元素信息
String toString(int[] a)
// 将指定值填充到数组之中
void fill(int[] a, int[] val)
// 将数组转化为List集合
Arrays.asList()
2)注意:在使用数组转集合的方法时
- 数组的元素必须是引用类型对象,如果是基本数据类型,那么转化成的List中就只有一个元素,就是数组本身。而不是数组中的元素。
- 这个方法会将数组和集合连接起来,如果当更新其中一个的时候,另一个也会更新。
生成的List集合不支持 add,remove 方法,如果强行使用,则报异常:UnsupportedOperationException
public class ArraysTest {
public static void main(String[] args) {
String[] strs = new String[]{"aaa", "bbb", "ccc", "ddd"};
List<String> list = Arrays.asList(strs);
System.out.println(list); // [aaa, bbb, ccc, ddd]
strs[0] = "eee";
System.out.println(list); // [eee, bbb, ccc, ddd]
list.add("fff");
System.out.println(list); // java.lang.UnsupportedOperationException
// 基本类型数据的数组
int[] ints = {1, 2, 3, 4};
List<int[]> list1 = Arrays.asList(ints);
// 这个时候,list1集合只有一个元素,那就是ints数组本身
System.out.println(list1.get(0)[1]); //2
}
}
三 面向对象
1、面向过程:是以执行者的角度来思考问题,侧重于实现的过程,侧重于怎么做,比较适合解决小型问题 2、面向对象:是以指挥者的角度来思考问题,侧重于整体的实现,侧重于谁来做,具体的实现过程不是很注重,比较适合解决大型问题。
3.1 Java类及类成员
3.1.1 Java类别
3.1.1.1 普通类
1)类是对一类事物的描述,是抽象的、概念上的定义。
2)对象是实际存在的该类事物的每个个体,因而也称为实例(instance)。
3)在Java中万物皆对象。
3.1.1.2 接口
接口的本质是一种契约、标准、规范;接口是与类并且的结构。
1)JDK7及之前,接口中只能存在全局变量(public static final)和抽象方法(public abstract),这两者的括号里的修饰都可以缺省,默认为此修饰。JDK8,接口新增了可以定义静态方法(static)和默认方法(default)。jdk9,新增可以定义私有方法(private)。
2)使用关键字 **interface**
来定义
3)接口中的所有成员变量默认都是**public static final**
修饰的
4)接口中的所有抽象方法默认都是 **public abstract **
修饰的
5)接口中没有构造器
6)接口中采用多继承机制
3.1.1.3 抽象类
随着继承层次中一个个新子类的定义,类变得越来越具体,而父类则更一般,更通用。类的设计应该保证父类和子类能够共享特征。有时将一个父类设计得非常抽象,以至于它没有具体的实例,这样的类叫做抽象类。
1)用abstract关键字来修饰一个类,这个类叫做抽象类。
2)用abstract来修饰一个方法,该方法叫做抽象方法。
3)抽象方法:只有方法的声明,没有方法的实现。以分号结束。
public abstract void talk();
4)含有抽象方法的类必须被声明为抽象类。
5)抽象类不能被实例化。抽象类是用来被继承的,抽象类的子类必须重写父类的抽象方法,并提供方法体。若没有重写全部的抽象方法,仍为抽象类。
6)不能用abstract修饰变量、代码块、构造器。
7)不能用abstract修饰私有方法、静态方法、final的方法、final的类。
3.1.2 类的成员
3.1.2.1 属性
1)语法格式
修饰符 数据类型 属性名 = 初始化值 ;
说明1: 修饰符
常用的权限修饰符有:private、缺省、protected、public
其他修饰符:static、final (暂不考虑)
说明2:数据类型
任何基本数据类型(如int、Boolean) 或 任何引用数据类型。
说明3:属性名
属于标识符,符合命名规则和规范即可。
- 例子
在方法体外,类体内声明的变量称为成员变量。在方法体内部声明的变量称为局部变量。public class Person{
private int age; // 声明private变量 age
public String name = "Lila"; // 声明public变量 name
}
2)成员变量(属性):
- 类变量
①在类中定义,使用static关键字修饰,是类共享的变量,当这个类有多个对象的时候,也是共享同一个类变量,可以通过类名.变量的形式直接调用。
②当类被类加载器加载的时候,在被加载过程中的第二阶段链接阶段中的准备阶段进行初始化,默认值为零值。
③在内存中存放在方法区中。
- 实例变量
①没有static修饰,是对象独立的,每个对象都有自己独立。
②随着对象的创建,在堆中分配空间,并默认初始化。
3)局部变量
- 形参(方法、构造器中定义的变量)
- 方法局部变量(在方法内)
- 代码块局部变量(在代码块定义)
4)成员变量和局部变量的区别
①成员变量是由默认值的
byte\short\int\long 默认值为: 0
float\double 默认值为:0.0
boolean 默认值为:false
char 默认值为:0或\u0000
引用数据类型的成员变量的默认值为:null
②局部变量没有默认值,必须手动初始化!在栈中分配内存
成员变量 | 局部变量 | |
---|---|---|
声明的位置 | 直接声明在类中 | 方法形参或内部、代码块内、构造器内等 |
修饰符 | private、public、static、final等 | 不能用权限修饰符修饰,可以用final修饰 |
初始化值 | 有默认初始化值 | 没有默认初始化值,必须显式赋值,方可使用 |
内存加载位置 | 堆空间 或 静态域内 | 栈空间 |
5)变量的值传导机制
1、如果赋值操作针对的是基本数据类型的变量,则将基本数据类型变量传递的是:保存的数据值
2、如果赋值操作针对的是引用数据类型的变量,则将引用数据类型变量传递的是:保存的地址值
3.1.2.2 方法
1)语法格式
修饰符 返回值类型 方法名(参数类型 形参1, 参数类型 形参2, ….){
方法体程序代码;
return 返回值;
}
2)静态方法
(1)静态方法不能被重写。
(2)当子类中写了一个和父类同名同参的静态方法时,使用谁的类名调用就是调用谁的方法。当父类的引用指向子类的对象时,这时候的父类引用调用同名静态方法时,此时调用的是父类的静态方法。
3)方法的重载(overload)
概述:在同一个类中,允许存在一个以上的同名方法,只要它们的参数个数或者参数类型不同即可。多个同名的方法之间就称为方法的重载。
- 两同一不同
(1)同方法名
(2)同一个类中
(3)不同的参数列表:参数类型或者参数个数不同
- 其他和重载无关项
(1)返回值类型
(2)方法修饰符
(3)形参名
4)方法的重写(override)
概述:子类在继承父类之后,可以重写父类中同名同参的方法。
- 注意:
(1)子类重写的方法中的修饰符权重不能小于父类中被重写的方法的修饰符权重
(2)子类不能重写父类中被private修饰的方法,如果子类扩展了这样一个与父类同名同参的方法,这种情况不构成重载
(3)如果父类中的方法返回值类型为void,那么子类中的方法返回值类型也必须为void
(4)如果父类返回值类型为引用类型,那么子类中方法的返回值类型必须为父类中返回值类型或者其子类
(5)父子类中构成重写的方法必须同为static或者非static
(6)子类不能重写父类的构造方法
5)递归方法
递归方法:一个方法体内调用它自身。
(1)方法递归包含了一种隐式的循环,它会重复执行某段代码,但这种重复执行无须循环控制。
(2)循环结构,要避免出现死循环。对于递归方法而言,避免方法持续不断的执行而不能终止!
(3)递归一定要向已知方向递归,否则这种递归就变成了无穷递归,类似于死循环
(4)递归的使用是浪费时间和空间的,不建议使用
(5)应用场景:①斐波那契数列,②汉诺塔
private int fun(int n) {
if (n <= 1) {
return 1;
} else {
return fun(n - 1) + n;
}
}
6)方法的值传导机制
- 当形参是基本数据类型变量是,传递的是:保存的数据值。
- 当形参是引用数据类型变量是,传递的是:保存的地址值。
7)可变形参的方法
(1)jdk5.0新增的特性
(2)可变形参的格式:数据类型 … 形参名。比如:String … str
(3)在调用时,可以接收的参数的个数为:0个,1个,2个,。。
(4)与同名不同参数的方法(除第5点除外)之间,也构成方法的重载
(5)与同名方法且参数同样类型的数组结构不构成重载。换句话说,两个方法不同在类中同时声明。
(6)如果有多个形参,则可变形参必须声明为最后一个参数
(7)最多只能声明一个可变形参
public class ArgsTest {
public static void main(String[] args) {
fun1("lz", "aa", "bb", "cc");
}
public static void fun1(String name, String... friend) {
System.out.println(
"[ " + name + " ] " + "==> "
+ friend[0] + " "
+ friend[1] + " "
);
}
}
3.1.2.3 构造器
1)作用:创建对象,初始化对象属性
2)特点:
- 构造函数必须和类的名字相同
- 每个类都可以有多个构造函数
- 构造函数可以有个参数
3)构造器重载:因为构造器并无返回值类型,所以同一类下的构造器都会构成重载关系
4)this:理解为当前对象或当前正在创建的对象
5)super:可以理解为父类对象
6)使用
当我们没有显式的声明类中的构造器时,系统会默认提供一个无参的构造器
构造器声明的格式:权限修饰符 类名(形参列表){}
当我们显式的定义类的构造器以后,系统就不再提供默认的无参的构造器了
在类中,至少会存在一个构造器
一个类的多个构造器之间也构成了重载。
一个有n个构造器,那么至多有(n-1)个构造器在首行调用了this()方法
3.1.2.4 代码块
1)静态方法块
- 代码块如果有修饰的话,只能使用static
- 内部可以声明执行语句
- 随着类的加载而执行
- 由于类的加载只执行一次,所以静态代码块也只执行一次
作用:用来初始化类的基本信息:静态属性
- 可以在内部调用类的静态结构:属性、方法。不能调用非静态的属性或方法
- 如果在一个类中声明了多个静态代码块,则按照声明的先后顺序执行。
- 静态代码块的执行要先于非静态代码块的执行
2)非静态代码块:
- 内部可以声明执行语句
- 随着对象的创建而执行
- 每创建一个对象,就执行一次
- 用来初始化对象的基本信息:非静态属性
- 可以在内部调用类的静态结构和非静态的结构:属性、方法
- 如果在一个类中声明了多个非静态代码块,则按照声明的先后顺序执行。
3.1.2.5 内部类
我们可以在一个类A的内部定义另一个类B,则此时类B就称为类A的内部类,类A就称为类B的外部类。一共分为4种:成员内部类,局部内部类,匿名内部类,静态内部类
1、成员内部类
- 这个是最普通的内部类,直接在类A中定义类B。内部类B可以无限制地访问外部类A的属性方法,包括私有的。但是如果AB两类中有相同名称的属性方法,则外部类的会被隐藏。如果想继续访问同名的外部类属性,则需要加上
**外部类.this.成员变量**
- 外部类访问内部类的成员,则需要先创建内部类B的对象,然后再通过对象访问
- 内部类B是依附于外部类A存在的,所以创建内部类B的对象时,必须同时创建一个外部类A的对象
- 成员内部类的修饰符可以是:private,protected,default,public。但是外部类只能是public,default
2、局部内部类(方法内,代码块内,构造器内)public class Test {
public static void main(String[] args) {
Outter outter =new Outter();
Outter.Inner inner = outter.new Inner(); //必须通过Outter对象来创建
}
}
局部内部类是定义在一个方法或者一个作用域里面的类,它和成员内部类的区别在于局部内部类的访问仅限于方法内或者该作用域内。
局部内部类就像是方法里面的一个局部变量一样,是不能有public、protected、private以及static修饰符的。
3、匿名内部类
匿名内部类是唯一一种没有构造器的类。正因为其没有构造器,所以匿名内部类的使用范围非常有限,大部分匿名内部类用于接口回调。匿名内部类在编译的时候由系统自动起名为Outter$1.class。一般来说,匿名内部类用于继承其他类或是实现接口,并不需要增加额外的方法,只是对继承方法的实现或是重写。
4、静态内部类
- 静态内部类也是定义在另一个类里面的类,只不过在类的前面多了一个关键字static
- 静态内部类是不需要依赖于外部类的,这点和类的静态成员属性有点类似,并且它不能使用外部类的非static成员变量或者方法
- 在没有外部类的对象的情况下,可以创建静态内部类的对象,如果允许访问外部类的非static成员就会产生矛盾,因为外部类的非static成员必须依附于具体的对象。
3.2 面向对象的三大特性
3.2.1 封装
1、概述
使用者对类内部定义的属性(对象的成员变量)的直接操作会导致数据的错误、混乱或安全性问题。在面向对象程式设计方法中,封装(英语:Encapsulation)是指一种将抽象性函式接口的实现细节部份包装、隐藏起来的方法。
2、作用
通过对属性修饰为private,实现对属性的封装,可以提供get,set方法对外开放访问和修改的方法。
3.2.2 继承
1)子类可以通过关键字:extends来继承父类,可以继承到父类中非私有的成员属性和方法
2)继承的格式: class A extends B
3)说明
- 子类在继承父类以后,就获取了父类中声明的属性、方法。对于父类中声明为private的结构,在子类继承父类以后,是可以获取到了的,只是由于封装性的影响,我们在子类中不可以直接调用。
- 此外,子类还可以在父类的基础上,定义自己额外的属性或方法,此时的子类、父类的关系不同于子集与集合的关系。extends:扩展、延展。
3.2.3 多态
1)概述:
多态性,理解为一个事物的多种形态。而Java中的多态性,可以理解为子类对象的多态性:父类的引用指向子类的对象。
注意:多态性,只适用于方法,不适用于属性!
2)方法调用
正常时的方法调用
Person e = new Person();
e.getInfo();
Student e = new Student();
e.getInfo();
虚方法的调用
子类中定义了与父类同名同参数的方法,在多态情况下,将此时父类的方法称为虚拟方法,父类根据赋给它的不同子类对象,动态调用属于子类的该方法。这样的方法调用在编译期是无法确定的。
Person e = new Student();
e.getInfo(); // 调用Student类的getInfo()方法
3)编译时类型和运行时类型
- 编译时e为Person类型,而方法的调用是在运行时确定的,所以调用的是Student类的getInfo()方法。——动态绑定。
- 编译时,引用变量只能调用声明的类型中的结构,在真正调用时,实际执行的是子类重写父类的方法。
- 总结为:编译看左边,运行看右边。
3.3 其他关键字
3.3.1 权限修饰符
被修饰符修饰的结构只能被特定位置的结构访问
1)private:同类
2)(default):同类,同包
3)protected:同类,同包,子类
4)public:任意类
3.3.2 super、this
①在Java类中使用super来调用父类中的指定操作:
1)super可用于访问父类中定义的属性
2)super可用于调用父类中定义的成员方法
3)super可用于在子类构造器中调用父类的构造器
②注意:
1)尤其当子父类出现同名成员时,可以用super表明调用的是父类中的成员。
2)super的追溯不仅限于直接父类。
3)super和this的用法相像,this代表本类对象的引用,super代表父类的内存空间的标识。
③调用父类的构造器
1)子类中所有的构造器默认都会访问父类中空参数的构造器。
2)当父类中没有空参数的构造器时,子类的构造器必须通过this(参数列表)或者super(参数列表)语句指定调用本类或者父类中相应的构造器。同时,只能”二选一”,且必须放在构造器的首行。
3)如果子类构造器中既未显式调用父类或本类的构造器,且父类中又没有无参的构造器,则编译出错。
No | 区别点 | this | super |
---|---|---|---|
1 | 访问属性 | 访问本类中的属性,如果本类没有此属性则从父类中继续查找 | 直接访问父类中的属性 |
2 | 调用方法 | 访问本类中的方法,如果本类没有此方法则从父类中继续查找 | 直接访问父类中的方法 |
3 | 调用构造器 | 调用本类构造器,必须放在构造器的首行 | 调用父类构造器,必须放在子类构造器的首行 |
3.3.3 static
使用范围:在Java类中,可用static修饰属性、方法、代码块、内部类
- 随着类的加载而加载
- 优先于对象存在
- 修饰的成员,被所有对象所共享
- 访问权限允许时,可不创建对象,直接被类调用
3.3.4 main方法
- 由于Java虚拟机需要调用类的main()方法,所以该方法的访问权限必须是public,又因为Java虚拟机在执行main()方法时不必创建对象,所以该方法必须是static的,该方法接收一个String类型的数组参数,该数组中保存执行Java命令时传递给所运行的类的参数。
- 又因为main() 方法是静态的,我们不能直接访问该类中的非静态成员,必须创建该类的一个实例对象后,才能通过这个对象去访问类中的非静态成员,这种情况,我们在之前的例子中多次碰到。
3.3.5 final
在Java中声明类、变量和方法时,可使用关键字final来修饰,表示“最终的”。
1)final标记的类不能被继承。提高安全性,提高程序的可读性。
- String类、System类、StringBuffer类
2)final标记的方法不能被子类重写。
- 比如:Object类中的getClass()。
3)final标记的变量(成员变量或局部变量)即称为常量。名称大写,且只能被赋值一次。
- final标记的成员变量必须在声明时或在每个构造器中或代码块中显式赋值,然后才能使用。
3.4 对象实例化过程
四 Java位运算
4.1 原码、反码、补码
4.1.1原码
1)正数的原码就是它的本身
假设使用一个字节存储整数,整数10的原码是:0000 1010
2)负数用最高位是1表示负数
假设使用一个字节存储整数,整数-10的原码是:1000 1010
4.1.2 反码
1)正数的反码和原码一样
假设使用一个字节来存储正数,那么正数10的反码就是:0000 1010
2)负数的反码是:按这个负数的原码来按位取反(0变1,1变0),符号位不变
假设使用一个字节来存储负数,那么这个负数-10的反码为:1111 0101
4.1.3 补码
整数的补码才是在计算机中的存储形式
1)正数的补码和原码一样
假设使用一个字节存储整数,整数10的补码是:0000 1010
2)负数的补码是:这个负数的反码 +1
假设使用一个字节来存储负数,那么这个负数-10的补码为:1111 0110
4.1.4 总结
1)正数的原码,反码,补码,三码合一,都是一样的
2)负数的原码是使用最高位表示负数,反码是原码按位取反,补码则是反码 +1
4.2 位运算
Java定义了位运算符,应用于整数类型(int),长整型(long),短整型(short),字符型(char),和字节类型(byte)等类型。 Java定义了七种位运算符。
>> | 右移运算符,符号左侧数值 按位右移 符号右侧数值指定的位数,若为正数则高位补0,若为负数则高位补1 |
---|---|
<< | 左移运算符,符号左侧数值 按位左移 符号右侧数值指定的位数,并在低位处补0 |
>>> | 无符号右移运算符,符号左侧数值 按位右移 符号右侧数值指定的位数,无论正负高位补0 |
& | 与(AND)运算符,对两个整型操作数中对应位执行布尔代数,两个位都为1时输出1,否则0 |
| | 或(OR)运算符,对两个整型操作数中对应位执行布尔代数,两个位中只要有一个为1就输出1,否则为0 |
^ | 异或(XOR)运算符,对两个整型操作数中对应位执行布尔代数,两个位相等则为0,不相等则为1 |
~ | 非(NOT)运算符,按位取反运算符翻转操作数的每一位,即0变成1,1变成0 |
int a = -20; // 补码为:0000 0000 0000 0000 0000 0000 0000 0000
int b = 30;
int result1 = a << 1; //-40
int result2 = a >> 1; //-10
int result3 = a >>> 1; //2147483638
int result4 = a & b; //12
int result5 = a | b; //-2
int result6 = a ^ b; //-14
int result7 = ~ a; //19