正所谓万丈高楼平地起,有了扎实的基础才能进阶更深奥的课程,才能让你后面的走得更轻松,学Java亦是如此!所以千万不能忽略基础的重要性,下面一起来温习一下那些容易忽略、容易混淆以及比较重要的Java基础。
1.JDK:Java Development Kit,java的开发和运行环境。 2.JRE:Java Runtime Environment,java程序的运行环境,java运行的所需的类库+JVM(java虚拟机)。 3.javac命令:当执行javac时,会启动java的编译器程序。对指定扩展名的.java文件进行编译。 生成了jvm可以识别的字节码文件。也就是class文件,也就是java的运行程序。 4. java命令:负责运行的部分,会启动jvm,加载运行时所需的类库,并对class文件进行执行。一个文件要被执行,必须要有一个执行的起始点,这个起始点就是main函数。
1.数据类型: (1). 8种基本类型:byte、short、int、long、float、double、char、boolean (2). 3种引用类型: 数组、类、接口。 级别从低到高为:byte,char,short(这三个平级)-->int-->float-->long-->double 自动类型转换:从低级别到高级别,系统自动转的; 强制类型转换:把一个高级别的数赋给一个别该数的级别低的变量就需要强转。
2、运算符: (1). & 和 &&区别:第一个叫逻辑与,第二个叫短路与。逻辑与是无论左边结果是什么,右边都参与运算;而短路与,如果在左边为false,那么右边就不参与运算了;显然短路与效率更高。| 和 || 的区别亦是如此。
(2). == 和 equals方法的区别:
(3).++和--: ++和--参与运算时,如果++(--)放在操作数后面,则先运算再自增(减),如果++(--)放在操作数前面,则先自增(减),再参与运算。
3、语句:
(1). switch循环:
String s = "牛逼";
switch (s){
case "牛逼":
System.out.println("牛逼");
break;
case "厉害":
System.out.println("厉害");
break;
default:
System.out.println("默认");
break;
}
这就是switch循环的用法了,switch支持的数据类型有byte、short、int、char、String以及Enum类型。
(2). while循环:
int x = 10;
while( x < 20 ) {
System.out.print("value of x : " + x );
x++;
}
(3).do while循环:
int x = 10;
do{
System.out.print("value of x : " + x );
x++;
}while( x < 20 );
do while和while的区别:do while至少执行一次。
break: 作用于switch ,和循环语句,用于跳出,或者称为结束。break语句单独存在时,下面不要定义其他语句,因为执行不到,编译会失败。当循环嵌套时,break只跳出当前所在循环。要跳出嵌套中的外部循环,只要给循环起名字即可,这个名字称之为标号。 continue: 只作用于循环结构,继续循环用的。结束本次循环,继续下次循环。该语句单独存在时,下面不可以定义语句,执行不到。
4、数组:用于存储同一类型数据的一个容器。 (1). 数组的初始化
int [] i = {1,2,3} //静态初始化
int [] arr = new int[5]; //动态初始化
arr[0] = 10;
(2).二分查找法(前提:数组中的元素必须有序) 二分查找法基本思想: 定义最大索引max,最小索引min,计算中间索引mid=(max+min)/2,拿要查找的数和中间索引处的数比较,如果中间索引处的数大了,就在左边找,这时最小索引min不变,最大索引max=mid-1,如果中间索引处的数小了,就在右边找,这时最大索引不变,最小索引min=mid+1。 代码实现:
public static int getIndex(int[] arr, int value) {
int min = 0;
int max = arr.length - 1;
int mid = (max + min) / 2;
while (arr[mid] != value) {
if (arr[mid] > value) {
max = mid - 1;
} else if (arr[mid] < value) {
min = mid + 1;
}
if (min > max) {
return -1;
}
mid = (max + min) / 2;
}
return mid;
}
(3).数组排序之冒泡排序: 冒泡排序基本思想: 相邻元素两两比较,大的往后放,这样一轮下来,最大值就出现在了最大索引处,如此这般继续,便可得到排好序的数组。每比完一次,下一次比较就可少比较一个元素,总共需要比较数组长度减一次。 代码实现:
for(int x=0; x<arr.length-1; x++){ // 外循环控制比较次数
for(int y=0; y<arr.length-1-x;y++){ // 内循环控制每一次的比较
if(arr[y]> arr[y+1]){
int temp= arr[y];
arr[y]= arr[y+1];
arr[y+1]=temp;
}
}
}
(4).数组排序之选择排序: 选择排序基本思想: 从0索引处开始,依次和后面的每一个比较,小的往前放,第一次比较完毕,最小值出现在0索引处,如此这般,便可得到排好序的数组。 代码实现:
for(int x=0; x<arr.length-1; x++){
for(int y=x+1; y<arr.length; y++){
if(arr[y]< arr[x]){
int temp = arr[x];
arr[x] = arr[y];
arr[y] = temp;
}
}
}
(5).数组逆序:
for(int start =0, end=arry.length-1; start <end ; start ++; end --){
int temp =arry[0];
arry[0] = arry[end];
arry[end] = temp;
}
5、Java内存:java分了5片内存。 1:寄存器。2:本地方法区。3:方法区。4:栈。5:堆。 栈:存储的都是局部变量 ( 函数中定义的变量,函数上的参数,语句中的变量 ); 堆:用于存储数组和对象,new出来的东西就存在堆内存中。
1、面向对象特点:
2、构造函数: 用于给对象进行初始化的函数,在对象创建时,就被调用,而且初始化动作只执行一次。所有对象创建时,都需要初始化才可以使用。 (1). 特点:
3、面向对象特性: (1).封装: 封装是指隐藏对象的属性和实现细节,仅对外提供公共访问方式。
(2).继承:从已有类得到继承信息创建新类的过程。
(3).多态:允许不同子类型的对象对同一消息做出不同的响应。
class 孔子爹 {
public int age = 40;
public void teach() {
System.out.println("讲解JavaSE");
}
}
class 孔子 extends 孔子爹 {
public int age = 20;
public void teach() {
System.out.println("讲解论语");
}
public void playGame() {
System.out.println("玩王者荣耀");
}
}
Java培训特别火,很多人来请孔子爹去讲课。这一天孔子爹被请走了,但是还有人来请,就剩孔子在家,价格还挺高。孔子一想,我是不是可以考虑去呢?然后就穿上爹的衣服,带上爹的眼睛,粘上爹的胡子,就开始装爹,跑到别人家里去讲课:
孔子爹 k爹 = new 孔子(); //向上转型
System.out.println(k爹.age); //40,对外表现的是父类的属性
k爹.teach(); //讲解论语,子类重写了teach方法,所以表现的是子类的,若没重写,则输出“讲解JavaSe”。
讲完了,下班回家了,脱下爹的装备,换上自己的装备,做回了自己:
孔子 k = (孔子) k爹; //向下转型
System.out.println(k.age); //20
k.teach(); //讲解论语
k.playGame(); //英雄联盟
4、抽象类和接口: (1). 抽象:将一类事物的共同特征或行为抽取出来,而不管是怎么实现的。 (2). 抽象类: 用abstract修饰的类。特点如下:
注意如下几点:
(3). 接口:是用关键字interface定义的。 接口中的成员都有固定的修饰符:
抽象类与接口区别:
5、内部类: 如果A类需要直接访问B类中的成员,而B类又需要建立A类的对象。这时,为了方便设计和访问,直接将A类定义在B类中就可以了。A类就称为内部类。内部类可以直接访问外部类中的成员。而外部类想要访问内部类,必须要建立内部类的对象。
class Outer{
int num = 4;
class Inner {
void show(){
System.out.println("inner show run "+num);// 直接访问外部类成员
}
}
public void method(){
Inner in = new Inner();//创建内部类的对象。
in.show();//调用内部类的方法。
}
}
匿名内部类就是内部类的简化写法,只调用一次时可以用匿名内部类。
6、异常: 异常分两种: (1). 编译时受检异常:只要是Exception及其子类都是编译时被检测的异常。 (2). 其中Exception有一个特殊的子类RuntimeException,以及RuntimeException的子类是运行异常,也就说这个异常是编译时不被检查的异常。 两种异常的区别:
1、this: 代表对象。就是所在函数所属对象的引用。哪个对象调用了this所在的函数,this就代表哪个对象,就是那个对象的引用。 2、Super: 代表是子类所属的父类中的内存空间引用。 3、static: 是一个修饰符,用于修饰成员(成员变量和成员函数)。
4、final:
1、线程与进程:
2、线程的随机性原理: 因为cpu的快速切换造成,哪个线程抢到了cpu的执行权,哪个线程就执行。
3、线程中常用方法: (1). 线程启动:使用start方法。调用start方法做了两件事:启动了线程;让jvm调用了run方法。 (2). 线程休眠: Thread.sleep(1000); // 休眠1秒钟。 (3). 线程礼让:Thread.yield(); // 使线程看上去更和谐,但不能保证你一次我一次。 (4). 给线程起名字:my1.setName("线程名")。 (5). 设置与获取线程优先级:my1.setPriority()和my1.getPriority();优先级默认是5,最大是10,最小是1。 (6). 守护线程:my1.setDaemon(true);my1设置为守护线程,表示该线程就是守护主线程的,主线程一退出my1也就死了。 (7). 线程中止:my1.interrupt(); (8). 线程加入:my1.join(); my2.start();// 表示my1走完了才轮到my2 (9). 获取当前线程名称:Thread.currentThread().getName();
4、线程状态:
5、实现多线程的方式: 三个窗口出售100张电影票,用两种方式实现: (1). 继承Thread类 ,由子类复写run方法。步骤:
public class sellTicket extends Thread{
//票要定义成静态成员,才能让三个线程共享。
private static int tickets=100;
public void run(){
while(true){
if(tickets>0){
Thread.sleep(100) //模拟延迟
System.out.print(Thread.currentThread().getName()+"正在出售第"+tickets-- +"张票");
}
}
}
public static void main(String[] args){
sellTicket s1=new sellTicket();
sellTicket s2=new sellTicket();
sellTicket s2=new sellTicket();
s1.setName("窗口1");
s2.setName("窗口2");
s3.setName("窗口3");
s1.start();
s2.start();
s3.start();
}
}
(2).实现Runnable接口。步骤:
public class sellTicket implements Runnable {
private int tickets=100;
public void run(){
while(true){
if(tickets>0){
Thread.sleep(100); //模拟延迟
System.out.print(Thread.currentThread().getName()+"正在出售第"+tickets-- +"张票");
}
}
}
public static void main(String[] args){
sellTicket st=new sellTicket();
Thread t1=new Thread(st,"窗口1");
Thread t2=new Thread(st,"窗口2");
Thread t3=new Thread(st,"窗口3");
t1.start();
t2.start();
t3.start();
}
}
6、同步: 运行上面两段卖票的程序会发现,票会出现负数或者是同一张票卖了好几次的情况,这就是线程安全问题。 (1).如何判断一个程序是否有线程安全问题?
满足以上3个条件,程序就有线程安全问题。
(2). 同步代码块:解决了线程安全问题,格式:
synchronized(锁对象){ // 锁对象可以是任意对象
// 多条语句操作共享数据的代码
}
改进上面的卖票案例:
public class sellTicket implements Runnable {
private int tickets=100;
Object obj = new Object(); // 锁对象
public void run(){
while(true){
synchronized(obj){
if(tickets>0){
Thread.sleep(100); //模拟延迟
System.out.print(Thread.currentThread().getName()+"正在出售第"+tickets-- +"张票");
}
}
}
}
}
(3).同步方法:其实就是将同步关键字定义在方法上,让方法具备了同步性。同步方法的锁对象是this;静态同步方法的锁对象是当前类的class文件。
(4).死锁:两个或两个以上的线程因争夺资源产生相互等待的现象。
(5).线程间的通信问题:
先看下面的例子(省略了student类):
设置学生信息的类:
public class SetStudentInfo implements Runnable {
private Student s;
public SetStudentInfo(Student s){
this.s=s;
}
public void run(){
synchronized(s){
s.setName("林青霞");
s.setAge(27);
}
}
}
获取学生信息的类:
public class GetStudentInfo implements Runnable {
private Student s;
public GetStudentInfo (Student s){
this.s=s;
}
public void run(){
synchronized(s){
System.out.println(s.getName()+"---"+s.getAge());
}
}
}
开启线程:
public class Test{
public static void main(String[] args){
Student s = new Student ();
SetStudentInfo setStudentInfo = new SetStudentInfo(s);
GetStudentInfo getStudentInfo = new GetStudentInfo(s);
Thread t1 =new Thread(setStudentInfo );
Thread t2 =new Thread(getStudentInfo );
t1.start();
t2.start();
}
}
多线程就是多条线程在抢cpu的执行权,上面的程序中,如果是t2先抢到执行权,那就有问题了。因为t2是获取学生信息,我都还没设置学生信息就获取,肯定出错。其实这就是生产消费模式。设置学生信息就是生产者,获取学生信息是消费者。正常的执行是这样的:
要实现这样的执行过程,Java提供了等待唤醒机制。Object类提供了3个方法:
因为这三个方法是通过锁对象调用,而锁对象又可以是任意对象,只有Object才可以表示任意对象,所以这三个方法定义在Object中。
使用等待唤醒机制改进上述案例:
student类中添加一个成员变量作为标记:
boolean flag; // true表示已生产,可以消费,false表示无产品可供消费
设置学生信息:
public class SetStudentInfo implements Runnable {
private Student s;
public SetStudentInfo (Student s){
this.s=s;
}
public void run(){
synchronized(s){
if(s.flag){ //true表示有
s.wait();//有就等待
}
//没有就生产
s.setName("林青霞");
s.setAge(27);
//修改标记并唤醒另一个线程
s.flag = true ; //生产了就修改标记为true
s.notify(); // 同时唤醒另一线程
}
}
}
唤醒以后并不是另一个线程就有了执行权,还是要抢,如果还是当前线程抢到,那么没关系,因为标记已经改成true ,当前线程就会等待。 获取学生信息:
public class GetStudentInfo implements Runnable {
private Student s;
public GetStudentInfo (Student s){
this.s=s;
}
public void run(){
synchronized(s){
if(!s.flag){ // 是false就等待
s.wait();
}
//被SetStudentInfo唤醒后就走到了这里
System.out.println(s.getName()+"---"+s.getAge());
s.flag = false(); //经过消费就没有了,修改为false
s.notify();// 通知生产者生产
}
}
}
上述代码就使用了等待唤醒机制。
为避免篇幅过长,本文先到此为止。本文总结了一些Java基础知识以及一个重点 —— 多线程。剩下的Java基础知识容我日后再整理。