7.16
String类
String类表示的字符串,但是它属于一个类,而不是基本数据类型,虽然像基本数据类型一样使用
两种实例化方式;
第一种方式:String str=”hello ”;
第二种方式:String str=new String(“hello”);
==表达式比较的是内存地址
想要判断两个字符串的内容是否相等可以使用String类中提供 equals()方法来完成
通过实例化方式完成的String类赋值 默认不支持常量池,但是可以通过intern()方法手动入池
字符串常量就是String的匿名对象
String实例化方式赋值的时候会存在两个对象 其中一个String类型的匿名对象做为垃圾空间存在
.java文件编译.class文件后 字符串类型的类型进入常量池中,
倘若String str = new String(“helloword”);则在常量池中生成一个helloword 但是new会开辟一个新的内存空间 而常量池的那个作为匿名垃圾对象存在;
字符串的内容一旦声明则不可改变
Stirng不可变性
String类常用方法
public int length()
2 public boolean isEmpty(); 判断字符串是否为空
3 public char charAt(int index);给定索引 反回索引的char字符
4 public byte[] getBytes(); 把一个字符串转换为byte[]数组
5 public boolean equals(Object anObject);判断两个字符串的内容是否相等
6 public boolean equalsIgnoreCase(String anotherString); 判断两个字符串内容是否相等 忽略大小写
7 public boolean startsWith(String prefix);判断字符串是否以某个字符串开头
8 public boolean endsWith(String suffix);判断字符串是否以某个字符串结尾
9 public int indexOf(int ch) public int indexOf(String str);从前往后找
10 public int lastIndexOf(int ch); 从后往前找
11 public String substring(int beginIndex) ;
12 public String concat(String str) ;拼接字符串
13 public boolean contains(CharSequence s);判断一个字符串是否存在
14 public String trim();去掉字符串的左右空格
15 public char[] toCharArray();把一个字符串变为字符数组
16 public static String valueOf(Object obj)把一个数据类型转换为一个字符串
17 split()分割字符串
String构造方法
1把字符数组准换为 字符串
String str =new String(字符数组数据类型/ 字节数组的数据类型)
2把byte数组转换为字符串
byte[] data=str.getBytes(); //转换为字节数组
String str1=new String(data,0,data.length); //又转换会字符串
this关键字
三个作用
1可以调用本类属性
2调用本类方法
3代表当前对象
继承:
继承的概念,在已有代码的基础上,进行扩展,可以不用编写重复的代码,父类更抽象,子类更具体被继承的类叫做父类,继承的类叫做子类
方法覆写的意义:
更具体的实现父类制定的标准
方法名称参数类型与个数和返回值和父类完全相同覆写的方法访问权限不能比父类更严格
继承的限制:
限制1在继承关系中只能存在单继承, (不能多层继承 但是可以多重继承)
7.17
1 this与 super关键字的区别?
This代表本类对象, 调用本类属性 本类方法
Super可以调用父类属性 和方法 但是不能代表父类对象
2代码块
普通块
构造快
静态块
多态:
方法的多态:
方法的重载' 覆写
方法的重载与方法的覆写的区别?
方法的重载:方法名称相同参数的类型与个数不同与返回值无关
方法覆写:方法名称参数类型与个数和返回值和父类完全相同覆写的方法访问权限不能比父类更严格
对象的多态:
向上转型统一标准,使用的方法向父类对齐
向下转型子类可以使用自己独特的方法
抽象类与接口的区别?
抽象类与接口的继承实现关系
包装类
在java的设计原则中,一切皆对象,但是程序中的基本数据类型是对象吗?例如 int double lang char
public static void main(String[]args){
//Int n=new Int(1); //把一个基本数据类型 变为了引用数据类型 //装箱
//System.out.println(n.intValue()); //把一个类变为了 基本数据类型 //拆箱
Integer n=new Integer(5); //装箱
System.out.println(n.intValue()); //拆箱
Integer n=5; //把一个基本数据类型 变为一个类 自动装箱
System.out.println(n); //自动拆箱 把一个类变为一个基本数据类型输出
}
在包装类中也存在了对象池的概念
数据转型:
包装类的优点在于可以编写操作方法,其中有一个方法为可以把一个字符串变为基本数据类型来使用
Integer类public static intparseInt(Stirng s) ; //把一个字符串变为int类型
Double类public static double parseDouble(String s)
Boolean public static boolean parseBoolean(String s)
在进行数据类型转换的时候必须给定转换的内容为(正确内容)如果给定的内容不是转换类型能接收的内容则会产生NumberFormatException异常
任何的数据类型和String操作都会向String靠拢
想把一个基本数据类型转换String类型则有两种方式
1拼接字符串
2使用String中的valueOf()方法完成
int n=5;
String str=String.valueOf(n);
System.out.println(str);
异常的处理
什么是异常?
异常导致程序中断一种指令流
如果发现程序异常则后面代码不在执行
处理异常
try{
(break/return)//不管这里是 break 或者是return finally永远执行
}catch(异常的类型 异常的对象){
处理异常 // 越是父类的异常越在下面 例如 exception放在最上面的catch后 //面会报错
}finally{// 作用 关闭资源 连接数据库
永远执行的代码
}jvm创建对象
在异常的继承结构中最大的父类为Throwable它下面存在两个子类(面试题)
Error表示 JVM程序出错, 虚拟机的层面上出错,Error是不能被异常处理的
Exception属于程序中的异常, 是可以被处理的
Exception继承结构中又分为两种异常 一种是运行时异常, 一种为普通的异常?
所谓运行时异常就是RunTimeException的子类 特点:在编译的时候不强制处理异常
(面试题)写出三个常见运行时异常:
NullPointException NumberFormatException IndexOutOfBoundsException
普通的异常特点:
在编译阶段必须强制处理或者抛出该异常
Throws关键字
定义在方法上使用,表示出现了异常暂不处理交给方法调用处处理
public static int sum(int y,int n)throws ArithmeticException{
return y/n;
}
Throw 关键字
其实所有的异常都是JVM进行实例化的,手动抛出一个异常的则必须使用new关键字完成
try {
throw new Exception("瞎抛的,管不着你"); //手动抛出的异常
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
自定义异常
虽然java中提供了大量的异常,但是可能这些异常都不满足开发者的需求,可以自己编写异常类
范例:编写一个自定义异常,一个类继承了Exception类这个类就可以作为一个异常类
package org.wubo.test;
public class MyException extends Exception{ //自定义异常类
public MyException(String str) {super(str);}}
包的访问权限:
在程序中的目的就是为文件进行分离,相当于操作系统中文件夹,在程序开发中肯定代码不是由一个人完成的
定义包的关键字packeg
命名命名规范
一般来说就是公司域名的反写
www.oracle.com包名称com.oracle.项目名称
Java中常用的包 (JDK提供的)
Java.lang程序的基本包 这个java.lang属于自动导入
Java.util 提供的工具类,常用日期集合操作类都在这个包里面
Java.text 文本类
Java.io Input Output 输入与输出
Java.sql数据库支持包
Java.net网络编程 soket
以下两个包接触不到
Java.awt 重量级图形界面
Java.swing轻量级图形界面
7.18
常用类库
StringBuilder
可以频繁修改字符串,在实际使用中,一般来说首选使用String来完成,只有在需要频繁小修改字符串的时候才使用StringBuilder 后者StringBuffer
以上两个类除了线程的安全性之外,其他的功能全部一样
public static void main(String[] args) {
String str="hello";
changeStr(str);
System.out.println(str);
}
public static void changeStr(StringBuilder str) {
str+="word";
}
str只有改变指向才会变
RunTime类
RunTime属于运行时的操作类,也是JVM运行时需要加载的类
RunTime类的方法
public native long totalMemory();取得总共的可用内存
public native long maxMemory(); 取得最大可用内存
public native long freeMemory(); 取得空闲内存
public native void gc(); 调用垃圾收集器,释放内存空间
范例:通过RunTime类取得JVM内存信息
System类
在程序以开始输出的时候就已经在使用这个类,属于系统类
System.out输出
System.err错误的输出
System.in输入
System类常用的方法
public static native void arraycopy(Object src, int srcPos,
Object dest, int destPos,
int length);
数组拷贝
Object src源数组 int srcPos 原数组开始拷贝的位置 Object desc目标数组 int destPos目标数组的开始位置 int length长度
int arr1[]=new int[] {1,2,3,4,5};
int arr2[]=new int[10];
System.arraycopy(arr1, 0, arr2, 0, arr1.length);
public static native long currentTimeMillis();取得当前的系统时间
范例:取得当前的系统时间
long currentTime=System.currentTimeMillis(); //取得当前的时间
System.out.println(currentTime);
Date date=new Date(currentTime);
System.out.println(date);
import java.util.Date;
public class SystemDemo {
public static void main(String[] args) {
long start=System.currentTimeMillis(); //取得当前的时间
String str="";
for(int i=0;i<10000;i++) {
str+="hello world";
}
long end=System.currentTimeMillis(); //获得结束时间
System.out.println("时间:"+(end-start)+"ms");
}
}
Finlize 作用
对象在创建的时候,存在构造方法表示对象属性的初始化
package org.wubo.test;
import java.util.Date;
public class SystemDemo {
public static void main(String[] args) {
Person per=new Person();
per=null;//指向改变成为垃圾对象gc回收相当于c++的析构函数
System.gc();
}
}
class Person{
public Person() {
System.out.println("出生了 地动山摇 满天乌云 代表妖孽出生了");
}
@Override
public void finalize() throws Throwable {
// TODO Auto-generated method stub
System.out.println("我真的还想在活500年");
}
}
日期类Date
主要掌握Date类与long类型相互转换的操作
Public Date(); 获取的当前时间
Pubic Date(long time)把long类型的时间转换为Date类型
Public long getTime() 把Date类型的时间转换为long类型
日期格式化类
SimpleDateFormat java.text
对于SimpleDateFormat只有两种操作
第一种
把Date类型格式化为字符串类型 ,格式化的格式由用户给定
SimpleDateFormat sd=new SimpleDateFormat("yyyy-MM-dd:HH:mm:ss");
String d=sd.format(date);
第二种
把字符串日期格式转换为Date类型格式
String d=sd.format(date);
随机数类
Random 主要目的产生随机数
AtomicLong 原子数据
public static void main(String[] args) {
Random ran=new Random();
for(int i=0;i<10;i++) {
System.out.println(ran.nextInt(100)); //输出0~10范围内的随机数 输出10次}}
public static void main(String[] args) {
UUID uid=UUID.randomUUID();//4b96f005-b9b1-42e8-a989-b45a42123099
System.out.println(uid);}
Math类
java为用户提供的一些数学操作类,其中四舍五入的操作以及PI的定义
public static void main(String[] args) {
System.out.println(Math.round(15.5)); //16
System.out.println(Math.round(-15.6)); //-16
System.out.println(Math.PI);
}
大数字操作类
package org.wubo.test;
import java.math.BigInteger;
import java.util.Random;
import java.util.UUID;
public class RandomTest {
public static void main(String[] args) {
int max=Integer.MAX_VALUE;
System.out.println(max);
BigInteger bigA=new BigInteger(max+"");
BigInteger bigb=new BigInteger(max+"");
System.out.println( bigA.add(bigb)); //两个大整数相加
System.out.println(bigA.subtract(bigb)); //两个大整数相减的方法
System.out.println(bigA.multiply(bigb)); //两个大整数相乘
System.out.println(bigA.divide(bigb)); //相除
}
}
Arrays类
数组的工具类,提供了一系列的数组相关的操作方法
Arrays类中提供的方法
BinarySearch()二分查找
int[]arr=new int[]{1,2,3,4,5,6,7,8,9};
int key=Arrays.binarySearch(arr, 3);
System.out.println(key);
toSting()理解该方法
几个易混淆的使用方法
1 String.valueOf();返回值:string
--调用者:string字符串
--被调用者 :任何对象,任何数组,任何类型(前提有tostring方法)
--作用 :将被调用者以字符串的形式输出
2. Arrays.tostring();返回值:string
--调用者:Arrays
--被调用者:存储着任何对象的数组,对象要自带tostring方法
--作用:将数组中的对象按照对象的tostring方法一个一个输出来
3. 对象.tostring();返回值:string
--调用者:各种对象
--没有被调用对象,直接使用方法
--作用:若对象自定义了tostring方法,那么按照对象定义的方法输出,若是对象没有重写方法,则按照输出哈希值
Fill();给定数组 为其填充内容
int[]arr=new int[10];
Arrays.fill(arr, 20);
for(int i=0;i
System.out.println(arr[i]);
}
数组的比较
使用Arrays.equals进行数组的排序
int[]arr=new int[] {1,2,3};
int[]arr2=new int[] {1,2,3};
boolean flag=Arrays.equals(arr, arr2);
对象数组排序
通过比较器进行排序
Comparable 重写compareTo方法
@Override
public int compareTo(Object o) { //该方法只需要反回3个值 0 -1 1
// TODO Auto-generated method stub
Person p=null;
if(o instanceof Person) {
p=(Person)o;
}
if(this.age>p.age) {
return 1;
}else if(this.age
return -1;
}
return 0;
}
Comparator重写compare
@Override
public int compare(Person2 p1,Person2 p2) {
if (p1.getAge()>p2.getAge()) {
return 1;
}else if (p1.getAge()
return -1;
}
return 0;
}
7.20 -7.21
1 进程与线程的区别
进程:在操作系统的定义中指的是一次命令完整运行
2进程与线程的区别?
线程是在进程的基础上上进一步划分:打开一个QQ.exe代表启动一个进程,在QQ可以聊天,聊天的同时可以传输文件,并且还可以语音聊天 在一个QQ进程中启动多个线程
例如:打开一个QQ启动一个进程,在QQ上打开聊天 启动一个线程,在打开一个传输数据,又启动一个线程
从以上的结论中可以发现;在一个进程中可以存在多个线程:而线程是可以同时运行的
3多线程的优点:
效率高:可以最大限度的利用CPU的空闲资源,比如一边编辑文档,一边打印文档中的内容
4 如何使用多线程
在java中存在两种方式可以完成多线程的使用,第一种方式就是 (继承)Thread类 ,在java中编写多线程程序的时候必须存在一个线程的主体类,而这个主体类只要继承了Thread类就可以完成多线程的功能
使用了start()方法启动了多线程之后,发现定义的3个线程在执行的时候 不是顺序执行了,而是随机的访问CPU的资源,过程为哪个线程先抢到资源哪个线程就先执行 线程的执行是存在随机性的
在主体方法main方法中把复杂的任务2作为一个单独的线程启动,这样就不会影响任务1 月任务3 的执行效率
通过观察start()方法的源码发现:
同一个实例的线程只能启动一次,如果启动超过一次则会抛出异常IllegalThreadStateException()
继续观察源码发现start()方法会调用一个start0()这个方法而这个方法的定义如下
private native void start0();
其中出现了native关键字,表示调用本地系统函数
原理如下:
在线程启动的时候,线程会抢占CPU的资源,CPU的资源调度,需要本地操作系统的支持
以上图一定要明白清楚的自己的可以画出来,面试过程中会存在为什么启动一个线程调用是start()方法而不是run()方法
线程的第二种实现方式实现Runnable接口
为什么存在第二种方式,第一种方式继承Thread类有什么不好
最大的问题:java中存在单继承局限,如果一个类继承了Thread类则这个类就没有办法继承别的类
所以为了解决以上的问题,可以使用第二种方式,实现Runnable接口,在接口中没有单继承的局限,一个类可以实现多个接口
观察Thread类的构造方法可以发现 构造方法的参数为Runnable接口对象,也就是说Thread类的构造方法可以接收Runnble接口的对象
在多态中讲过,在接口中一个父接口可以接收所有的子类对象
例如Runable run=new MyThread();应为MyThread类实现了Runnable接口作为Runnable接口的子类 则可以通过构造方法传入给Thread类
通过以上的操作就可以把MyThread传入给Thread类 进而通过Thread类中的start()方法启动一个线程
注意:无论何时启动一个线程必须使用Thread类中的start()方法
Thread类与Runnable接口的关系
在多线程的实现中现在已经了两种方式,Thread类与Runnable接口,从代码上来讲肯定首选使用Runnable接口,应为可以很好的避免单继承局限,可以为程序做更多的扩展
通过源码分析过其实Thread类也是实现的Runnable接口,接下来观察一下他们两个的关系
多线程开发的本质其实就是多个线程同时对一个资源的抢占,Thread主要描述的就是线程,资源是通过Runnable接口完成的
扩展:Lambda表达式
利用Lmbda可以简化在面向对象过程中的开发,但是前提是只有一个抽象方法的接口或者类才可以使用
Lambda表达式是JDK1.8的新特性,利用此形式可以实现函数式编程(一个接口或者一个类只存在一个抽象方法) 前提下就可以简化编写方式
Scala 语言中就提供了函数式编程
在面上对象过程中,一直存在的问题,即使编写简单的代码,同样需要进行类的完整定义
public interface IMessage {
// 只存在一个抽象方法
public void send(String str);
}
public static void main(String[] args) {
IMessage msg=(str)->{
System.out.println("发送消息"+str);
};
msg.send("hello");
}
对于Lmbda表达式提供了几个格式如下:
1 方法没有参数:()->{};
2 方法有参数:(参数1,参数2)->{};
3 Thread thread =new Thread(()->{});
线程第三种实现方式实现Callable接口
从传统的开发角度上来说实现一个多线程肯定使用Runnable接口,但是Runnable接口有一个缺点:没有返回值
在Jdk1.5之后推出了callable接口可以给一个线程执行完毕反回结果,接下来观察callableji接口的定义
Public interface callable
在callable接口内设置了一个泛型操作,其实这个泛型就是规定了需要反回的数据类型
有这个泛型的支持就不会造成向下转型出现错误
public class MyThread implements Callable{
@Override
public String call() throws Exception {
// TODO Auto-generated method stub
//实现线程的业务方法 类似run方法
for(int i=0;i<10;i++) {
System.out.println("线程执行i="+i);
}
return "执行完毕"; //与run方法不同的是存在了返回值
}}
package org.callable.demo;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
public class ThreadTest {
public static void main(String[] args) throws InterruptedException, ExecutionException {
FutureTask task=new FutureTask<>(new MyThread());
new Thread(task).start();
//通过Thread启动线程之后 可以通过FutureTask存在get方法可以取得call()方法中的返回值
String result=task.get();
System.out.println(result);
}
}
面试题:Callable接口 与Runnable接口的区别?
Runnable接口没有返回值, Callable接口可以在执行多线程任务之后返回数据类型
线程的状态:(面试题)
(面试题)
1 任何一个线程对象都是通过Thread类进行封装,调用start()方法进入到就绪状态,就绪状态线程并没有执行
2线程进入到就绪状态之后,等到资源调度,抢到资源的线程先执行,先执行的线程就进入到运行状态
3 线程在执行的过程可能会存在阻塞状态,当用户调用类似sleep或者wait 方法会进入到阻塞状态
4终止状态,当线程执行完毕,或者调用线程的停止的方法线程就会进入到终止状态
线程的常用操作方法:
所有线程的操作方法基本上都定义在Thread类中
1线程的命名与取得
线程的运行状态是不确定,如果在不确定的线程中操作一个线程只能依靠线程的名字,线程的名字是非常重要的概念,在Thread方法中提供有线程的命名与取得的方法
1构造方法: public Thread(Stirng name);
2 public final String getName(); 取得线程的名称
3 public final void setName(String name); 设置线程名称
通过以上的三个方法可以完成线程的命名与取得,但是并不能通过以上方法取得线程当前的对象
Thread类中提供了
1取得当前线程的方法 public static Thread currentThread();
为线程设置名称和取得名称在以上的代码中直接在main方法输出当前线程的名称输出为main说明main方法也是一个线程 在JVM启动的时候会启动两个线程一个是main方法另一个是GC
public class RunnableDemo03 {
public static void main(String[] args) {
// 为线程命名
MyThread03 mt=new MyThread03();
Thread t1=new Thread(mt);
t1.setName("线程A");
t1.start();
System.out.println(Thread.currentThread().getName());
}
}
2线程的休眠
public static native void sleep(long millis) throws InterruptedException;
如果现在希望一个程序跑的慢一点,或者说暂缓执行,可以使用Thread类中提供的sleep()方
完成
该方法需要的参数long类型的毫秒时间
范例:使用Sleep方法暂缓线程
@Override
public void run() { //覆写Runnable接口中的run方法
// TODO Auto-generated method stub
//编写线程的功能
for(int i=0;i<30;i++) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(title+"="+i);
}
}
Runnable run=()->{
for(int i=0;i<10;i++) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("线程"+i);}};
new Thread(run).start();
3 线程的终断
在Thread类中里面提供了线程的终断的方法
public static boolean interrupted()
通过以上方法可以让一个叫做正在休眠的线程终断休眠
public static boolean interrupted()
以上方法可以判断一个线程是否已经被打断,已经被打断线程不能再次被打断
范例:线程的终断
package org.thread.demo;
public class ThreadDeme03 {
public static void main(String[] args) {
Thread thread=new Thread(()->{
System.out.println("薛哲同学憋了72个小时需要输出");
try {
Thread.sleep(100000); //输出100秒
System.out.println("我只想安静的蹲在这里抽一根烟");
} catch (InterruptedException e) {
// TODO Auto-generated catch block
System.out.println("谁也别来烦我");
e.printStackTrace();
}
});
thread.start(); //启动线程 开始输出
if(!thread.isInterrupted()) { //判断是这个线程是否被打断过 如果为true 没有
System.out.println("李哲 嘿嘿一乐 想要偷偷去打断一下");
thread.interrupt(); //打断线程
}
}
}
4 线程的强制执行
所谓线程的强制执行,指的是当一个线程满足条件之后,会一直占用资源直到这个线程执行完毕之后,其他线程在去访问资源
使用join关键则
public final void join() throws InterruptedException
5 线程的礼让//就算你礼让了 对面不走 还是自己走
public static native void yield();
所谓线程的礼让就是在执行的时候当前不执行,先让别的线程执行.在Thread中提供了一个
6线程的优先级
线程的执行的时候是可以设置优先级的,理论上来说谁的优先级高谁就先执行,但是在线程中优先级也是一个叫做相对的概念,而不是绝对的。
1设置优先级
public final void setPriority(int newPriority)
public final int getPriority() ;
通过以上代码,可以对线程设置优先级,但是发现在设置优先级之后线程的执行也不是按照优先级执行的,优先级只会对线程增加执行的概率而已
范例:取得主线程的优先级
System.out.println(Thread.currentThread().getPriority());
线程的同步与死锁
在多线程中Thread描述的是一个个线程对象,而Runnable描述的是资源,当多个线程对象同时访问同一资源,操作不当的情况下会产生数据的错误
同步问题的引出:
以上的问题为:多个对象同时访问一个资源的时候,由于多个对象在抢占资源,有可能多个对象同一时间对资源进行修改,这样就造成了数据的不同步
如果想要让线程不出现数据错误的现象则需要在操作的资源上加上同步锁,则这样之后每一个线程对象在操作的资源的时候后面线程对象会等到之前的线程对象操作完毕之后才会进行资源的操作
同步和异步:
异步:多个线程对象可以同一时间对资源进行访问
同步:同一时间只能有一个线程在操作资源,剩下的线程只能等待上一个线程操作完毕之后在进行资源的操作
性能上的区别:
同步:由于每个线程只能单独进行资源的操作,所以效率不高
异步:异步处理由于多个线程可以同时进行资源的访问效率很高但是不安全
现在问题已经分析出来了。主要的原因是异步和同步的问题,如果想要解决以上的问题则需要在操作资源上进行同步锁的添加
在多线程中可以用两种方式实现同步锁:
关键字:synchronized这个单词必须能默写出来
通过以上的关键字就可以实现两种方式:
1同步代码块
synchronized {}
2同步方法
public synchronized boolean safe()
队列又分为顺序队列和循环队列
顺序队列存在假溢出的问题
由于顺序队列存在假溢出的问题,所以可以使用循环队列解决 特点:队头和队尾首尾相连
在循环队列中解决假溢出的问题:
判断是否为空队列
计数元素的个数
少使用一个空间
少使用一个空间的情况下:
(队尾+1)%maxSize=front(队头)