Java入门
文章目录
前言
记录学习的过程
Java基础编程
1语言概述
IDE(Integrated Development Environment)=集成开发环境
JDK=JRE+java开发工具(javac.exe,java.exe,javadoc.exe)
JRE=JVM+Java核心类库
常见命令行操作:cd,md,rd,del,cd…,cd/
环境变量:
希望在任何路径下都执行java的开发工具
JAVA_HOME=bin的上一层目录
path=%JAVA_HOME%\bin
javac:编译阶段,win命令,大小写无所谓
java:运行阶段,java命令
编译生成的.class结尾的字节码文件不包含注释的信息
文档注释(java特有):可以被javadoc所解析
多行注释不可以嵌套使用
API:应用程序 编程 接口
2基本语法
关键字和标识符
关键字:全部小写
标识符规则:大小写字母,0-9,_,$,数字不能开头
标识符的命名规范有哪些?
包名:xxxyyyzzz
类名、接口名:XxxYyyZzz
变量名、方法名:xxxYyyZzz
常量名:XXX_YYY_ZZZ
变量的使用
数据类型:基本&引用
基本数据类型:byte short int long char float double boolean
long类型变量必须以l或L结尾
float类型变量必须以f或F结尾
引用数据类型:类、接口、数组
自动类型提升:表示数范围的大小,小->大
byte、short、char ——int ——long ——float—— double
byte,char,short做运算自动转化为int
强制类型转换:(),可能导致精度损失
String可以和8种基本数据类型做连接运算,结果仍是String类型
字符串比较要用s1.equals(s2),返回值是boolean类型
进制
进制表示:二0b,八0,十六0x
计算机底层用补码方式存储数据
运算符
取余运算:结果的符号与被模数的符号相同
自增自减,+=,-=不会改变本身变量的数据类型
赋值运算符:支持连续赋值
||与&&:要考虑短路
位运算符:操作整型,<<×2,>>÷2
输入输出
Scanner s=new Scanner(System.in)
字符串next——字符char=s.chatAt(0),
整型nextInt,浮点型nextDouble,
随机数:double a=Math.random()
范围[0.0,1.0)
流程控制
包含:顺序,分支,循环
switch-case
switch的表达式不可以是boolean类型和浮点类型
case后面必须是常量
break&continue
break:switch-case 和 循环结构(结束当前循环),其后不可以声明执行语句
continue: 循环结构(结束当次循环),其后不可以声明执行语句
for(;;;)循环
分号隔开的每部分可以有多个语句,用,隔开
3数组
创建数组对象会开辟一整块空间
同类型数组的赋值 ≈ 电脑创建快捷方式
一维数组
声明+初始化:
int [] a=new int[]{1,2,3,4}; //静态初始化,其中new int[]可省略
int [] a=new int[4]; //动态初始化
数组的长度——arr.length
默认初始化值:
整型 : 0
浮点型:0.0
char:0
boolean :false
引用类型:null
内存的简化结构:
下面是实例
二维数组
声明+初始化:
int [] a[]=new int[][]{
{1,2,3},{4,5},{6,7}}; //静态初始化,其中new int[]可省略
int [][] a=new int[4][3]; //动态初始化,其中列数可以省略
默认初始化值:
初始化一:
外层元素的值:地址值
内层元素的值:与一维数组相同
初始化二:
外层元素的值:null
内层元素的值:不能调用,否则报错。
下面是二维数组的内存解析:
涉及的常用算法:排序,查找
十大排序算法:
选择排序:直接选择排序、堆排序(不稳定)nlog2n
交换排序:冒泡排序(小——大)、快速排序(不稳定)nlog2n
插入排序:直接插入排序、折半插入排序、Shell排序
归并排序(稳定)nlog2n,桶式排序,基数排序
Arrays工具类
Boolean equals(int[] a,int[] b)
String toString(int[] a)
void fill(int[] a,int val)
void sort(int[] a)
int binarySearch(int[] a,int key)
复制数组 vs 赋值操作
复制数组 | 赋值操作 |
---|---|
int[] arr1 = new int[arr.length];for(;;;) |
array2 = array1; |
4面向对象(上)
JVM内存结构
虚拟机栈:即平时提到的栈结构。我们将局部变量存储在栈结构中.
堆:我们将new出来的结构(比如:数组、对象)加载在堆空间中。补充:对象的属性/成员变量(非static的)加载在堆空间中。
方法区:类的加载信息、常量池、静态域
类和对象:
类:抽象的、概念上的内容
对象:实实在在存在的一个个体。
对象是由类的实例。
“万事万物皆对象”:
1.在Java中,将功能、结构等封装到类中,通过类的实例化,调用具体的功能结构
Scanner,String等
文件:file
网络资源:URL
2.涉及到与前端html、后端的数据库交互时,前后端的结构与Java层面的交互,都体现为类、对象。
三条主线:
① 类及类的成员:属性、方法、构造器;代码块、内部类
② 面向对象的三大特征:封装、继承、多态、(抽象性)
③ 关键字:this,super,abstract,interface,static,final,package,import
面向过程&面向对象:
面向过程 | 面向对象 |
---|---|
强调功能 | 强调具备功能的对象 |
以函数为最小单位 | 以类/对象为最小单位 |
考虑怎么做 | 考虑谁来做 |
对象的内存解析:
属性:
成员变量(类的属性) | 局部变量 | |
---|---|---|
不同点 | 位置:类的{}中 | 位置:其他 |
可以使用权限修饰符 | 不可以使用权限修饰符 | |
有默认初始化值 | 没有默认初始化值,一定要显性赋值 | |
加载到堆空间中~非static | 加载到栈空间中 |
方法:
描述类中具有的功能
声明:
public void eat(){
System.out.println("好吃");
}
重载:
“两同一不同”:同一个类、相同方法名;参数列表不同
参数传递机制:
基本数据类型:数据值
引用数据类型:地址值 (含变量的数据类型)
封装:
体现一:将类的属性xxx私化(private),同时,提供公共的(public)方法来获取(getXxx)和设置(setXxx)此属性的值
体现二:不对外暴露的私有的方法
体现三:单例模式(将构造器私有化)
体现四:如果不希望类在包外被调用,可以将类设置为缺省的。
权限修饰符:
权限从小到大顺序为:private < 缺省 < protected < public
4种权限都可以用来修饰类的内部结构:属性、方法、构造器、内部类
修饰类的话,只能使用:缺省、public
构造器:
作用:
- 创建对象
Person p=new Person();
- 初始化对象的信息
Person p=new Person("elsa");
与类同名
如果没显式的定义类的构造器的话,则系统默认提供一个空参的构造器
定义构造器的格式:权限修饰符 类名(形参列表){},不声明返回值
一个类中,至少会有一个构造器。
关键字this,package,import:
可以调用的结构:属性、方法;构造器
this:
理解为当前对象
1.调用属性/方法
通常情况下,我们都择省略"this."。
特殊情况下,如果构造器的形参和类的属性同名时,我们必须显式的使用"this.变量"的方式
2.调用构造器
构造器中,可以显式的使用"this(形参列表)"方式,调用本类中指定的其他构造器
"this(形参列表)"必须声明在当前构造器的首行
package:
声明在源文件的首行
属于标识符,遵循标识符的命名规则、规范(xxxyyyzzz)
每"."一次,就代表src下一层文件目录
import:
使用import结构导入指定包下的类、接口
声明在包的声明和类的声明之间
可以使用"xxx.*"的方式,表示可以导入xxx包下的所有结构(不包含子包)
使用的类或接口是本包下定义的,则可以省略import结构
5面向对象(中)
继承
好处:
1.减少了代码的冗余,提高了代码的复用性
2.便于功能的扩展
3.为之后多态性的使用,提供了前提
Java中类的单继承性:一个类只能有一个父类
方法重写
子类继承父类以后,可以对父类中同名同参数的方法,进行覆盖操作.
规则:
① 子类重写的方法的方法名和形参列表与父类被重写的方法的方法名和形参列表相同
② 子类重写的方法的权限修饰符不小于父类被重写的方法的权限修饰符,不能重写private
③ 返回值类型:
父类是void或者基本数据类型,返回值相同
父类是A类型,返回值可以是A类或A类的子类
④ 子类重写的方法抛出的异常类型不大于父类被重写的方法抛出的异常类型
⑤都要声明为非static的(声明为static的-非重写)
重写和重载
1.概念 2.规则 3.重写体现多态性 4.
重载 | 重写 |
---|---|
同名不同参 | 同名同参 |
同一个类 | 子类重写父类 |
非多态性 | 多态性 |
早绑定:调用地址在编译期绑定 | 晚绑定:调用地址在运行期绑定 |
关键字:super
super 关键字可以理解为:父类的
可以用来调用的结构:属性、方法、构造器
当子类和父类中定义了同名的属性时,使用"super.属性"的方式
当子类重写了父类中的方法以后,使用"super.方法"的方式
super(形参列表)"的使用,必须声明在子类构造器的首行
this(形参列表)"或"super(形参列表)“只能二选一,不能同时出现
至少一个类的构造器中使用了"super(形参列表)”,调用父类中的构造器
多态性
可以理解为一个事物的多种形态。
对象的多态性:父类的引用指向子类的对象
举例:
Person p = new Man();
Object obj = new Date();
对象的多态性,只适用于方法,不适用于属性
使用方法:虚拟方法调用
有了对象的多态性以后,我们在编译期,只能调用父类中声明的方法,但在运行期,我们实际执行的是子类重写父类的方法。
总结:编译,看左边;运行,看右边。
向上转型&向下转型
向上转型:多态
向下转型:编译时,只能调用父类中声明的属性和方法。子类特有的属性和方法不能调用
instanceof的使用:
a instanceof A:判断对象a是否是类A的实例。如果是,返回true;如果不是,返回false。
a所属的类与类A必须是子类和父类的关系,否则编译错误。
Object类
1.Object类是所有Java类的根父类
2.默认父类为java.lang.Object类
3.方法:
方法名 | |
---|---|
equals() | 1.只能适用于引用数据类型,比较两个对象的地址值是否相同 |
2.String、Date、File、包装类等都重写了Object类中的equals()方法,比较两个对象的"实体内容"是否相同。 | |
3.应用中通常需要对Object类中的equals()进行重写. | |
== | 1.比较的是基本数据类型变量:比较两个变量保存的数据是否相等。 |
2.比较的是引用数据类型变量:比较两个对象的地址值是否相同 | |
toString() | 1.String、Date、File、包装类等都重写了Object类中的toString()方法。 |
2.通过重写返回"实体内容"信息 |
包装类的使用
为了使基本数据类型的变量具有类的特征,引入包装类。
Integer、Character
基本数据类型<—>包装类:
JDK 5.0 新特性:自动装箱 与自动拆箱
基本数据类型、包装类—>String:
String s=String.valueOf(int i);
String—>基本数据类型、包装类:
int i=Integer.parseInt(String s);
面试题
8.1 谈谈你对多态性的理解?
1.实现代码的通用性。
2.Object类中定义的
public boolean equals(Object obj){ }
JDBC:使用java程序操作(获取数据库连接、CRUD)数据库(MySQL、Oracle、DB2、SQL Server)
③ 抽象类、接口的使用肯定体现了多态性。(抽象类、接口不能实例化)
重写和重载
equals()与==
final、finally、finalize的区别?
6面向对象(下)
关键字:static,final,abstract,interface
static:静态的
修饰属性、方法、代码块、内部类、不可修饰构造器
static修饰属性:静态变量(或类变量)
① 静态变量随着类的加载而加载。可以通过"类.静态变量"的方式进行调用
② 静态变量的加载要早于对象的创建。
③ 由于类只会加载一次,则静态变量在内存中也只会存在一份:存在方法区的静态域中。
static修饰方法:静态方法、类方法
① 随着类的加载而加载,可以通过"类.静态方法"的方式进行调用
② 静态方法中,只能调用静态的方法或属性
非静态方法中,既可以调用非静态的方法或属性,也可以调用静态的方法或属性
③在静态的方法内,不能使用this关键字、super关键字
如何判定属性和方法应该使用static关键字:
关于属性
属性是可以被多个对象所共享的,不会随着对象的不同而不同的。
类中的常量也常常声明为static
关于方法
操作静态属性的方法,通常设置为static的
工具类中的方法,习惯上声明为static的。 比如:Math、Arrays、Collections
final:最终的
修饰:类、方法、变量
修饰一个类:此类不能被其他类所继承。
比如:String类、System类、StringBuffer类
修饰方法:表明此方法不可以被重写
比如:Object类中getClass();
修饰变量:此时的"变量"就称为是一个常量
static final 用来修饰属性:全局常量
abstract:抽象的
修饰:类、方法
抽象类
①此类不能实例化
②抽象类中一定有构造器,便于子类实例化时调用(涉及:子类对象实例化的全过程)
③开发中,都会提供抽象类的子类,让子类对象实例化,完成相关的操作 —>抽象的使用前提:继承性
抽象方法:
①抽象方法只方法的声明,没方法体
②包含抽象方法的类,一定是一个抽象类。反之,抽象类中可以没有抽象方法的。
③子类重写父类中的所有的抽象方法后,此子类方可实例化
1.abstract不能用来修饰:属性、构造器等结构
2.abstract不能用来修饰私方法、静态方法、final的方法、final的类
interface:接口
Java中,接口和类是并列的两个结构
JDK7及以前:只能定义全局常量和抽象方法
全局常量:public static final的.但是书写时,可以省略不写
抽象方法:public abstract的
JDK8:除了定义全局常量和抽象方法之外,还可以定义静态方法、默认方法
Java开发中,接口通过让类去实现(implements)的方式来使用.
Java类可以实现多个接口 —>弥补了Java单继承性的局限性
格式:class AA extends BB implements CC,DD,EE
接口与接口之间可以继承,而且可以多继承
接口的具体使用,体现多态性
接口,实际上可以看做是一种规范
面向接口编程:
项目的具体需求是多变的,我们必须以不变应万变才能从容开发,此处的不变就是规范.
代理模式
代理模式是Java开发中使用较多的一种设计模式。代理设计就是为其他对象提供一种代理以控制对这个对象的访问。
比如:明星&经纪人
工厂模式
简单工厂模式:用来生产同一等级结构中的任意产品。(对于增加新的产品,需要修改已有代码)
工厂方法模式:用来生产同一等级结构中的固定产品。(支持增加任意产品)
抽象工厂模式:用来生产不同产品族的全部产品。(对于增加新的产品,无能为力;支持增加产品族)
模板方法的设计模式
软件开发中实现一个算法时,整体步骤很固定、通用,这些步骤已经在父类中写好了。但是某些部分易变,易变部分可以抽象出来,供不同子类实现。这就是一种模板模式。
应用场景:
单例模式
所谓类的单例设计模式,就是采取一定的方法保证在整个的软件系统中,对某个类只能存在一个对象实例。
饿汉式:
private static Bank instance = new Bank();
懒汉式:
private static Order instance = null;
饿汉式 | 懒汉式 |
---|---|
线程安全 | 线程不安全 |
对象加载时间过长 | 延迟对象的创建 |
代码块
用来初始化类、对象的信息
代码块要是使用修饰符,只能使用static
静态代码块 | 非静态代码块 |
---|---|
随类加载执行,只执行一次 | 随对象创建而执行 |
只能调用静态的结构 | 能调用静态/非静态 |
实例化子类对象时,涉及到父类、子类中静态代码块、非静态代码块、构造器的加载顺序:
由父及子,静态先行。
内部类:
Java中允许将一个类A声明在另一个类B中
成员内部类 | 局部内部类 |
---|---|
静态/非静态 | 方法内/构造器内/代码块内 |
外部类$内部类名.class | 外部类$数字 内部类名.class |
课后练习:
声明抽象类,并包含抽象方法。测试类中创建一个继承抽象类的匿名子类的非匿名对象
abstract AA{
public abstract void m();
}
main(){
AA a = new AA(){
public void m(){
}};
a.m();
}
如何创建静态成员内部类和非静态成员内部类的对象?
Person static Dog Bird
静态:
Person.Dog dog = new Person.Dog();
非静态:
Person p = new Person();
Person.Bird bird = p.new Bird();
抽象类和接口的异同?
抽象类 | 接口 |
---|---|
不能实例化 ,都可以包含抽象方法 | 同 |
可以定义抽象方法 | jdk8前只能定义全局常量和抽象方法,8后可定义静态方法和默认方法,9私有方法 |
类:单继承 | 接口:多继承 类与接口:多实现 |
有构造器 | 无构造器 |
异常处理
异常事件:
Error | Exception |
---|---|
jvm无法解决的严重问题,不处理 | 可以进行异常的处理 |
eg:JVM系统错误,资源耗尽 | eg:空指针,网络中断,数组越界 |
比如:栈溢出、堆溢出OOM |
异常分类:
编译时异常(check异常)—— 红色
运行时异常(uncheck异常)—— 蓝色
异常体系结构
java.lang.Throwable
|-----java.lang.Error:一般不编写针对性的代码进行处理。
|-----java.lang.Exception:可以进行异常的处理
|------编译时异常(checked)
IOException
FileNotFoundException
ClassNotFoundException
|------运行时异常(unchecked,RuntimeException)
NullPointerException
ArrayIndexOutOfBoundsException
ClassCastException
NumberFormatException
InputMismatchException
ArithmeticException
异常处理
抓抛模型
程序在正常执行的过程中,一旦出现异常,就会在异常代码处生成一个对应异常类的对象。并将此对象抛出。
关于异常对象的产生:
① 系统自动生成的异常对象
② 手动的生成一个异常对象,并抛出(throw)
异常处理方式一:try-catch-finally
try{
* //可能出现异常的代码
*
* }catch(异常类型1 变量名1){
* //处理异常的方式1
* }catch(异常类型2 变量名2){
* //处理异常的方式2
* }catch(异常类型3 变量名3){
* //处理异常的方式3
* }
* ....
* finally{
* //一定会执行的代码
* }
在try结构中声明的变量,再出了try结构以后,就不能再被调用
catch中的异常类型如果满足子父类关系,则要求子类一定声明在父类的上面。
finally是可选的
finally中声明的是一定会被执行的代码。
数据库连接、输入输出流、网络编程Socket等资源,JVM是不能自动的回收的,我们需要自己手动的进行资源的释放。此时的资源释放,就需要声明在finally中。
使用try-catch-finally处理编译时异常,是得程序在编译时就不再报错,但是运行时仍可能报错。
异常处理方式二:throws + 异常类型
写在方法的声明处
两种处理方法
try-catch-finally:真正的将异常给处理掉了。
throws的方式只是将异常抛给了方法的调用者。并没真正将异常处理掉。
开发中的选择
如果父类中被重写的方法没throws方式处理异常,则子类重写的方法也不能使用throws,意味着如果子类重写的方法中异常,必须使用try-catch-finally方式处理。
子类重写的方法抛出的异常类型不大于父类被重写的方法抛出的异常类型
执行的方法a中,先后又调用了另外的几个方法,这几个方法是递进关系执行的。我们建议这几个方法使用throws的方式进行处理。而执行的方法a可以考虑使用try-catch-finally方式进行处理。
除了自动抛出异常对象的情况之外,我们还可以手动的throw一个异常类的对象。
自定义异常类
1.继承于现的异常结构:RuntimeException 、Exception
2. 提供全局常量:serialVersionUID
3. 提供重载的构造器
public class MyException extends Exception{
static final long serialVersionUID = -7034897193246939L;
public MyException(){
}
public MyException(String msg){
super(msg);
}
}
面试题
throw 表示抛出一个异常类的对象,生成异常对象的过程。声明在方法体内。
throws 属于异常处理的一种方式,声明在方法的声明处。
Java高级编程
多线程
程序、进程、线程
程序 | 即指一段静态的代码。 |
---|---|
进程 -资源分配的单位 | 程序的一次执行过程 |
线程-调度和执行的单位 | 进程可进一步细化为线程,是一个程序内部的一条执行路径。 |
内存结构:
每个线程,拥有自己独立的:栈、程序计数器
多个线程,共享同一个进程中的结构:方法区、堆
并行&并发
– | – |
---|---|
并发 | 一个CPU(采用时间片)同时执行多个任务。比如:秒杀、多个人做同一件事 |
并行 | 多个CPU同时执行多个任务。比如:多个人同时做不同的事。 |
创建多线程的方式
继承Thread类
①重写run()方法
class MyThread extends Thread{
@Override
public void run() {
//过程
}
}
②创建Thread子类的对象并调用start()
Thread t1=new MyThread();
ti.start();
实现Runnable接口
class iThread implements Runnable{
@Override
public void run() {
//过程
}
}
②将实现Runnable子类的对象作为参数传递到Thread类的构造器中,创建Thread类的对象,并调用start()启动
Thread i1=new Thread(new iThread());
i1.start();
两种方式的对比
开发中:优先选择:实现Runnable接口的方式
原因:
实现的方式没类的单继承性的局限性
实现的方式更适合来处理多个线程共享数据的情况。
联系:public class Thread implements Runnable
相同点:两种方式都需要重写run(),将线程要执行的逻辑声明在run()中。
目前两种方式,要想启动线程,都是调用的Thread类中的start()。
Thread类常用方法
start():启动当前线程;调用当前线程的run()
run(): 通常需要重写Thread类中的此方法,将创建的线程要执行的操作声明在此方法中
currentThread():静态方法,返回执行当前代码的线程
getName():获取当前线程的名字
setName():设置当前线程的名字
yield():释放当前cpu的执行权
join():在线程a中调用线程b的join(),此时线程a就进入阻塞状态,直到线程b完全执行完以后,线程a才结束阻塞状态。
sleep(long millitime):在指定的millitime毫秒时间内,当前线程是阻塞状态。
isAlive():判断当前线程是否存活
stop():已过时。当执行此方法时,强制结束当前线程。
getPriority():获取线程的优先级
setPriority(int p):设置线程的优先级
线程通信:wait() / notify() / notifyAll() :此三个方法定义在Object类中的。
线程的生命周期
线程的同步机制
背景:卖票问题
引入:在Java中,我们通过同步机制,来解决线程的安全问题。