好久之前学的java
了,之前也没有总结性的学习,在此想总结性的重新学习一下。
0x00 Java的介绍
Java帝国的诞生
一场旷日持久的战争
C & C++
1972年C诞生
- 贴近硬件,运行极快,效率极高
- 操作系统,编译器,数据库,网络系统等
- 指针和内存管理
1982年C++诞生
- 面向对象
- 兼容C
- 图形领域、游戏等
反抗
- 我们要建立一个新的语言
- 语法有点像C
- 没有指针
- 没有内存管理
- 真正的可移植性,编写一次,到处运行
- 面向对象
- 类型安全
- 高质量类库
- ……
JAVA初生
1995年的网页简单而粗糙,缺乏互动性
图形界面的程序(Applet)
Bill Gate说:这是迄今为止设计的最好的语言!
Java 2标准版(J2SE):去占领桌面
Java 2移动版(J2ME):去占领手机
Java 2企业版(J2EE):去占领服务器
大量的巨头加入
Java的发展
他们基于Java平台开发了巨多的平台,系统,工具
- 构建工具:Ant,Maven,Jekins
- 应用服务器:Tomcat,Jetty,Jboss,Websphere,weblogic
- web开发:Struts,Spring,Hibernate,Mybatis
- 开发工具:Eclipse,Netbean,Idea,Jbuilder
- ……
2006:Hadoop(大数据领域)
2008:Android(手机端)
Java特性和优势
简单性、面向对象、可移植性、高性能、分布性、动态性、多线程、安全性、健壮性
Java三大版本
Write Once、Run Anywhere
JavaSE:标准版(桌面程序、控制台开发……)
JavaME:嵌入式开发(手机、小家电……)
JavaEE:企业级开发(web端、服务器开发……)
JDK、JRE、JVM
- JDK:Java Development Kit
- JRE:Java Runtime Environment
- JVM:Java Virtual Machine
0x01 Java基础语法
注释
- Java中的注释有三种:
- 单行注释
//
- 多行注释
/* 注释 */
- 文档注释
/** @Author y0lo */
- 单行注释
标识符
关键字
Java 所有的组成部分都需要名字。类名、变量名以及方法名都被称为关键字
标识符注意点
- 所有的标识符都应该以字母(A-Z或者a-z),美元符($)、或者下划线(_)开始
- 首字母之后可以是字母(A-Z或者a-z),美元符($)、或者下划线(_)或数字的任何字符组成
- 不能使用关键字作为变量名或方法名。
- 标识符是大小写敏感的
- 可以使用中文命名,但不建议,也不建议使用拼音
数据类型
强类型语言(Java是强类型语言)
- 要求变量的使用要严格符合规定,所有变量必须先定义后才能使用
弱类型语言
Java的数据类型分为两大类
- 基本类型(primitive type)
- 引用类型(reference type)
八大基本数据类型
- 整数:int、byte、short、long (Integer、Byte是一个类)
- 浮点数:float、double
- 字符:char (String不是基本数据类型,String是一个类)
- 布尔:boolean
数据类型扩展
整数扩展
- 二进制0b、八进制0、十六进制0x
浮点数扩展
1
2
3
4
5
6
7
8
9float f = 0.1f; // 0.1
double d = 1.0/10; // 0.1
System.out.println(f==d); // false
float d1 = 233333333333333333333f;
float d2 = d1 + 1;
System.out.println(d1==d2); // true- BingDecimal 数学工具类
字符扩展
1
2
3
4
5
6
7
8char c1 = 'a';
char c2 = '中';
System.out.println((int) c1); // 97
System.out.println((int) c2); // 20013
char c3 = '\u0061';
System.out.println(c3); // a- Unicode编码 2字节 0-65536
- 转移字符
- \t 制表符
- \n 换行符
String
1
2
3
4
5
6String sa = new String("hello,world");
String sb = new String("hello,world");
System.out.println(sa == sb); // false
String sc = "hello,world";
String sd = "hello,world";
System.out.println(sc == sd); // trueboolean
1
2
3boolean flag = true;
if(flag){}
if(flag==true){}
类型转换
由于Java是强类型语言,所以要进行有些运算的时候,需要用到类型转换。
1
2低 -------------------------------> 高
byte,short,char -> int -> long -> float -> double1
2
3int i = 128;
byte b = (byte)i; // 内存溢出
System.out.println(b); // -128注意点:
- 低 -> 高 自动转换
- 高 -> 低 需要强制转换
- 不能对布尔值进行转换
- 不能把对象类型转换为不相干的类型
- 转换的时候可能存在内存溢出,或者精度问题!
变量
- 变量是什么:就是可以变换的量!
- Java是一种强类型语言,每个变量都必须声明其类型。
- Java变量是程序中最基本的存储单元,其要素包括变量名,变量类型和作用域
注意事项:
- 每个变量都有类型,类型可以是基本类型,也可以是引用类型
- 变量名必须是合法的标识符。
- 变量声明是一条完整的语句,因此每一个声明都必须以分号结束。
变量作用域
- 类变量
- 实例变量
- 局部变量
1 | public class Demo(){ |
常量
- 常量(Constant):初始化(initialize)后不能再改变值!不会变动的值。
- 所谓常量可以理解成一种特殊的变量,它的值被设定后,在程序运行过程中不允许被改变。
1 | final 类型 常量名 = 常量值; |
变量的命名规范
- 所有变量、方法、类名:见名知意
- 类成员变量;首字母小写和驼峰原则:monthSalary 除了第一单词以外,后面的单词首字母大写 lastName
- 局部变量:首字母小写和驼峰原则
- 常量:大写字母和下划线:MAX_VALUE
- 类名:首字母大写和驼峰原则,Man,GoodMan
- 方法名:首字母小写和驼峰原则:run(),runRun()
运算符
- Java 语言支持如下运算符:
- 算术运算符:
+ , - , * , / , % , ++ , --
- 赋值运算符:
=
- 关系运算符:
> , < , >= , <= , == , != , instanceof
- 逻辑运算符:
&& , || , !
- 位运算符:
& , | , ^ , ~ , >> , << , >>>(了解!!)
- 条件运算符:
? :
- 扩展运算符:
+= , -= , *= , /=
- 算术运算符:
运算符优先级
包机制
为了更好地组织类,Java提供了包机制,用于区别类名的命名空间
包语句的语法格式为:
package pkg1[.pkg2.[pkg3]];
一般利用公司域名倒置作为包名;
为了能够使用某一包的成员,我们需要在Java程序中明确导入该包,使用 “import” 语句可以完成此功能
1
import package1[.package2...].(classname|*);
JavaDoc
- javadoc命令是用来生成自己API文档的
- 参数信息
- @author 作者名
- @version 版本号
- @since 知名需要最早使用的jdk版本
- @param 参数名
- @return 返回值类型
- @throws 异常抛出情况
0x02 Java流程控制
Scanner对象
之前我们学的基本语法中我们并没有实现程序和人的交互,但是Java给我们提供了这样一个工具类,我们可以获取用户的输入,java.util.Scanner 是 Java5 的新特征,我们可以通过 Scanner 类来获取用户的输入
基本语法:
1
Scanner s = new Scanner(System.in);
通过 Scanner 类的 next() 与 nextLine() 方法获取输入的字符串,在读取前我们一般需要 使用 hasNext() 与 hasNextLine() 判断是否还有输入的数据。
next()
- 一定要读取到有效字符后才可以结束输入
- 对输入有效字符之前遇到的空白,next()方法会自动将其去掉
- 只有输入有效字符后才将其后面输入的空白作为分隔符或者结束符
- next() 不能得到带有空格的字符串
nextLine()
- 以Enter为结束符,也就是说 nextLine() 方法返回的是输入回车之前的所有字符
- 可以获得空白
顺序结构
选择结构
- if单选择结构
- if双选择结构
- if多选择结构
- 嵌套的if结构
- switch多选择结构
- switch 语句中的变量类型可以是:
- byte、short、int 或者 char
- 从 Java SE7 开始 switch 支持字符串String类型了
- 同时 case 标签必须为字符串常量或字面量
- switch 语句中的变量类型可以是:
循环结构
while循环
do…while循环
for循环
在 Java 5 中引入了一种主要用于数组的增强型 for 循环
break、continue
- break 用于强制退出循环,不执行循环中剩余的语句(break 语句也在 switch 语句中使用)
- continue 语句用在循环语句体中,用于终止某次循环过程,即跳过循环中尚未执行的语句,接着进行下一次循环的判断
0x03 Java方法
方法定义
方法包括:修饰符、返回值类型、方法名、参数类型、方法体
方法调用
对象名.方法名(实参列表)
值传递和引用传递
String类型传递
1 | public static void main(String[] args) { |
运行的结果是
1 | AAA |
String是一个类,类是引用数据类型,做为参数传递的时候,应该是引用传递。但是从结果看起来却是值传递。
原因:
String的API中有这么一句话:“their values cannot be changed after they are created”,
意思是:String的值在创建之后不能被更改。
API中还有一段:
String str = “abc”;
等效于:
char data[] = {‘a’, ‘b’, ‘c’};
String str = new String(data);
也就是说:对String对象str的任何修改 等同于 重新创建一个对象,并将新的地址值赋值给str。
这样的话,上面的代码就可以写成:
1 | public static void main(String[] args) { |
方法的重载
- 重载的规则
- 方法名必须相同
- 参数列表必须不同(个数不同、或类型不同、参数排列顺序不同等 )
- 方法的返回类型可以相同也可以不相同
- 仅仅返回类型不同不足以成为方法的重载
命令行传参
有时候你希望运行一个程序时候再传递给它消息。这要靠命令行参数给main()函数实现
1
2
3
4
5
6
7public class CommandLine(){
public static void main(String args[]){
for(int i = 0; i < args.length; i++){
System.out.println("args[" + i + "]:" + args[i]);
}
}
}
可变参数
- JDK 1.5 开始,Java 支持传递同类型的可变参数给一个方法。
- 在方法声明中,在指定参数类型后加一个省略号(…)。
- 一个方法中只能指定一个可变参数,它必须是方法的最后一个参数。任何普通的参数必须在它之前声明。
1 | public static void printMax(double... numbers){ |
0x04 数组
声明数组
1 | dataType[] arrayRefVar; // 首选的方法 |
使用 new 操作符来创建数组
1
dataType[] arrayRefVar = new dataType[arraySize];
数组索引下标从 0 开始
获取数组长度:
arrays.length
内存
三种初始化
静态初始化
1
2int[] a = {1,2,3};
Man[] mans = {new Man(1,1), new Man(2,2)};动态初始化
1
2
3int[] a = new int[2];
a[0] = 1;
a[1] = 2;数组的默认初始化
- 数组是引用类型,它的元素相当于类的实例变量,因此数组一经分配空间,其中的每个元素也被按照实例变量同样的方法被隐式初始化。
数组的使用
普通的 For 循环
For-Each 循环 JDK 1.5后
1
2
3
4int arrays = {1,2,3,4,5};
for (int array : arrays){
System.out.println(array);
}数组作方法入参
数组作返回值
多维数组
二维数组
1
int a[][] = new int[2][5];
Arrays 类
数组的工具类 java.util.Arrays
具有以下常用功能:
- 给数组赋值:通过 fill 方法
- 对数组排序:通过 sort 方法
- 比较数组:通过 equals 方法比较数组中元素值是否相等
- 查找数组元素:通过 binarySearch 方法能对排序好的数组进行二分查找
0x05 面向对象
什么是面向对象
面向对象编程(Object-Oriented Programming OOP)
面向对象编程的本质就是:以类的方式组织代码,以对象的组织(封装)数据。
抽象
三大特性:
- 封装
- 继承
- 多态
从认识角度考虑是先用对象后有类。对象,是具体的事物。类,是抽象的,是对对象的抽象。
从代码运行角度考虑是先有类后有对象。类是对象的模板。
构造器
类中的构造器也称为构造方法,是在进行创建对象的时候必须要调用的。并且构造器有以下两个特点:
- 必须和类名同名
- 必须没有返回类型,也不能写void
封装
该露的露,该藏的藏
- 我们程序设计要追求“高内聚,低耦合”。
记住这句话就够了:属性私有,get/set
封装的意义:
- 提高程序的安全性,保护数据
- 隐藏代码的实现细节
- 统一接口
- 系统可维护性增加了
继承
继承的本质是对某一批类的抽象,从而实现对现实世界更好的建模
extends 的意思是“扩展”,子类是父类的扩展。
JAVA 中只有单继承,没有多继承!
继承的特性
- 子类拥有父类非 private 的属性、方法。
- 子类可以拥有自己的属性和方法,即子类可以对父类进行扩展。
- 子类可以用自己的方式实现父类的方法。
- Java 的继承是单继承,但是可以多重继承,单继承就是一个子类只能继承一个父类,多重继承就是,例如 B 类继承 A 类,C 类继承 B 类,所以按照关系就是 B 类是 C 类的父类,A 类是 B 类的父类,这是 Java 继承区别于 C++ 继承的一个特性。
- 提高了类之间的耦合性(继承的缺点,耦合度高就会造成代码之间的联系越紧密,代码独立性越差)。
多态
- 多态存在的三个必要条件
- 继承
- 重写
- 父类引用指向子类对象:Parent p = new Child();
抽象类
- 在 Java 语言中使用 abstract class 来定义抽象类
- 规定:
- 抽象类不能被实例化(初学者很容易犯的错),如果被实例化,就会报错,编译无法通过。只有抽象类的非抽象子类可以创建对象。
- 抽象类中不一定包含抽象方法,但是有抽象方法的类必定是抽象类。
- 抽象类中的抽象方法只是声明,不包含方法体,就是不给出方法的具体实现也就是方法的具体功能。
- 构造方法,类方法(用 static 修饰的方法)不能声明为抽象方法。
- 抽象类的子类必须给出抽象类中的抽象方法的具体实现,除非该子类也是抽象类。
接口
- 接口与类的相似点:
- 一个接口可以有多个方法
- 接口文件保存在
.java
结尾的文件中,文件名使用接口名 - 接口的字节码文件保存在
.class
结尾的文件中 - 接口相应的字节码文件必须在与包名称相匹配的目录结构中
- 接口与类的区别:
- 接口不能用于实例化对象
- 接口没有构造方法
- 接口中所有的方法必须是抽象方法,Java 8 之后 接口中可以使用
default
关键字修饰的非抽象方法 - 接口不能包含成员变量,除了
static
和final
常量 - 接口不是被类继承了,而是要被类实现
- 接口支持多继承
- 接口特性
- 接口中每一个方法也是隐式抽象的,接口中的方法会被隐式的指定为
public abstract
(只能是public abstract,其他修饰符都会报错) - 接口中可以含有变量,但是接口中的变量会被隐式的指定为
public static final
变量(并且只能是 public,用 private 修饰会报编译错误) - 接口中的方法是不能在接口中实现的,只能由实现接口的类来实现接口的方法
- 接口中每一个方法也是隐式抽象的,接口中的方法会被隐式的指定为
- 抽象类和接口的区别
- 抽象类中的方法可以有方法体,就是能实现方法的具体功能,但是接口中的方法不行
- 抽象类中的成员变量可以是各种类型的,而接口中的成员变量只能是
public static final
类型的。 - 接口中不能含有静态代码块以及静态方法(用 static 修饰的方法),而抽象类是可以有静态代码块和静态方法
- 一个类只能继承一个抽象类,而一个类却可以实现多个接口
内部类
成员内部类
成员内部类是最普通的内部类,它的定义为位于另一个类的内部,形如下面的形式:
1
2
3
4
5
6
7
8
9
10
11
12
13class Circle {
double radius = 0;
public Circle(double radius) {
this.radius = radius;
}
class Draw { //内部类
public void drawSahpe() {
System.out.println("drawshape");
}
}
}这样看起来,类Draw像是类Circle的一个成员,Circle称为外部类。成员内部类可以无条件访问外部类的所有成员属性和成员方法(包括private成员和静态成员)。
1
2
3
4
5
6
7
8
9
10
11
12
13
14class Circle {
private double radius = 0;
public static int count =1;
public Circle(double radius) {
this.radius = radius;
}
class Draw { //内部类
public void drawSahpe() {
System.out.println(radius); //外部类的private成员
System.out.println(count); //外部类的静态成员
}
}
}不过要注意的是,当成员内部类拥有和外部类同名的成员变量或者方法时,会发生隐藏现象,即默认情况下访问的是成员内部类的成员。如果要访问外部类的同名成员,需要以下面的形式进行访问:
1
2外部类.this.成员变量
外部类.this.成员方法虽然成员内部类可以无条件地访问外部类的成员,而外部类想访问成员内部类的成员却不是这么随心所欲了。在外部类中如果要访问成员内部类的成员,必须先创建一个成员内部类的对象,再通过指向这个对象的引用来访问:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18class Circle {
private double radius = 0;
public Circle(double radius) {
this.radius = radius;
getDrawInstance().drawSahpe(); //必须先创建成员内部类的对象,再进行访问
}
private Draw getDrawInstance() {
return new Draw();
}
class Draw { //内部类
public void drawSahpe() {
System.out.println(radius); //外部类的private成员
}
}
}成员内部类是依附外部类而存在的,也就是说,如果要创建成员内部类的对象,前提是必须存在一个外部类的对象。创建成员内部类对象的一般方式如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29public class Test {
public static void main(String[] args) {
//第一种方式:
Outter outter = new Outter();
Outter.Inner inner = outter.new Inner(); //必须通过Outter对象来创建
//第二种方式:
Outter.Inner inner1 = outter.getInnerInstance();
}
}
class Outter {
private Inner inner = null;
public Outter() {
}
public Inner getInnerInstance() {
if(inner == null)
inner = new Inner();
return inner;
}
class Inner {
public Inner() {
}
}
}内部类可以拥有 private 访问权限、protected 访问权限、public 访问权限及包访问权限。比如上面的例子,如果成员内部类 Inner 用 private 修饰,则只能在外部类的内部访问,如果用 public 修饰,则任何地方都能访问;如果用 protected 修饰,则只能在同一个包下或者继承外部类的情况下访问;如果是默认访问权限,则只能在同一个包下访问。这一点和外部类有一点不一样,外部类只能被 public 和包访问两种权限修饰。我个人是这么理解的,由于成员内部类看起来像是外部类的一个成员,所以可以像类的成员一样拥有多种权限修饰。
局部内部类
局部内部类是定义在一个方法或者一个作用域里面的类,它和成员内部类的区别在于局部内部类的访问仅限于方法内或者该作用域内。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18class People{
public People() {
}
}
class Man{
public Man(){
}
public People getWoman(){
class Woman extends People{ //局部内部类
int age =0;
}
return new Woman();
}
}注意: 局部内部类就像是方法里面的一个局部变量一样,是不能有 public、protected、private 以及 static 修饰符的。
匿名内部类
匿名内部类应该是平时我们编写代码时用得最多的,在编写事件监听的代码时使用匿名内部类不但方便,而且使代码更加容易维护。下面这段代码是一段 Android 事件监听代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16scan_bt.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
// TODO Auto-generated method stub
}
});
history_bt.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
// TODO Auto-generated method stub
}
});这段代码为两个按钮设置监听器,这里面就使用了匿名内部类。这段代码中的:
1
2
3
4
5
6
7new OnClickListener() {
public void onClick(View v) {
// TODO Auto-generated method stub
}
}就是匿名内部类的使用。代码中需要给按钮设置监听器对象,使用匿名内部类能够在实现父类或者接口中的方法情况下同时产生一个相应的对象,但是前提是这个父类或者接口必须先存在才能这样使用。当然像下面这种写法也是可以的,跟上面使用匿名内部类达到效果相同。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21private void setListener()
{
scan_bt.setOnClickListener(new Listener1());
history_bt.setOnClickListener(new Listener2());
}
class Listener1 implements View.OnClickListener{
public void onClick(View v) {
// TODO Auto-generated method stub
}
}
class Listener2 implements View.OnClickListener{
public void onClick(View v) {
// TODO Auto-generated method stub
}
}这种写法虽然能达到一样的效果,但是既冗长又难以维护,所以一般使用匿名内部类的方法来编写事件监听代码。同样的,匿名内部类也是不能有访问修饰符和 static 修饰符的。
匿名内部类是唯一一种没有构造器的类。正因为其没有构造器,所以匿名内部类的使用范围非常有限,大部分匿名内部类用于接口回调。匿名内部类在编译的时候由系统自动起名为 Outter$1.class。一般来说,匿名内部类用于继承其他类或是实现接口,并不需要增加额外的方法,只是对继承方法的实现或是重写。
静态内部类
静态内部类也是定义在另一个类里面的类,只不过在类的前面多了一个关键字static。静态内部类是不需要依赖于外部类的,这点和类的静态成员属性有点类似,并且它不能使用外部类的非static成员变量或者方法,这点很好理解,因为在没有外部类的对象的情况下,可以创建静态内部类的对象,如果允许访问外部类的非static成员就会产生矛盾,因为外部类的非static成员必须依附于具体的对象。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17public class Test {
public static void main(String[] args) {
Outter.Inner inner = new Outter.Inner();
}
}
class Outter {
public Outter() {
}
static class Inner {
public Inner() {
}
}
}
0x06 异常机制
- 三种类型的异常:
- 检查性异常:最具代表的检查性异常是用户错误或问题引起的异常,这是程序员无法预见的。例如要打开一个不存在文件时,一个异常就发生了,这些异常在编译时不能被简单地忽略。
- 运行时异常: 运行时异常是可能被程序员避免的异常。与检查性异常相反,运行时异常可以在编译时被忽略。
- 错误: 错误不是异常,而是脱离程序员控制的问题。错误在代码中通常被忽略。例如,当栈溢出时,一个错误就发生了,它们在编译也检查不到的。
- Exception 类的层次
- 所有的异常类是从 java.lang.Exception 类继承的子类。
- Exception 类是 Throwable 类的子类。除了Exception类外,Throwable还有一个子类Error 。
- Java 程序通常不捕获错误。错误一般发生在严重故障时,它们在Java程序处理的范畴之外。
- Error 用来指示运行时环境发生的错误。
- 异常类有两个主要的子类:IOException 类和 RuntimeException 类。
- 本文作者: y0lo
- 本文链接: http://example.com/2022/03/13/java基础/
- 版权声明: 本博客所有文章除特别声明外,均采用 MIT 许可协议。转载请注明出处!