JAVA
一、基础语法
冯·诺依曼体系结构
1.基础数据类型
- 整数:byte(1位)、short、int、long
- 浮点型:float、double
- 字符:char(2位),可存一个汉字
- boolean:true/false(默认值是false)(1为)
Java中整数默认为int,浮点数默认为double
JDK7.0新特性:数字可以用_
分割,输出正常数字
int money = 100_000_000;
二进制在数字前加0b、八进制在数字前加0、十六进制在数字前加0x
银行业务不能用浮点数进行计算,因为有精度损失和舍入误差,还有范围会溢出 (最好完全避免使用浮点数进行比较)
一般都是使用BigDecimal进行计算
2.数据转换
- 强制转换:高–>低
- 自动转换:低–>高
- 注意:
- 不能对布尔值进行转换
- 不能把对象转化成不相干的类型
- 转化时可能存在内存溢出或精度问题
定义常量
final static int PI = 1; //final和static是修饰符,前后顺序不影响
3.运算符
- instanceof:判断一个对象是否是一个类的实例
- &&和||:有短路的特性,&&如果前面错误就不会计算后面的表达式了,||是前面正确就不会计算后面的了
- 整数相加,如果没有long型,结果全为int
4.方法
- 重载:方法名一样,参数(个数、类型、顺序)不一致为重载,与返回值无关
- 可变参数(不定项同类型参数):一个方法只能有一个可变参数,且只能是形参的最后一个参数
public class Main {
public static void main(String[] args) {
Main main = new Main();
main.test(1,2,3,4);
}
public void test(int... a){
for (int i:a){
System.out.println(i);
}
}
}
- 递归:两个部分 (java是栈机制)
- 递归头:什么时候不调用自己,如果没有头,就会死循环
- 递归体:什么时候调用自己
//5! 5*4*3*2*1
//1! 1
//2! 2*f(1)
//3! 3*f(2)
public int f(int n){
if(n == 1){
return 1;
}else{
return n*f(n);
}
}
- 内存分析:
- 栈:存放基本变量,定义的变量(引用)(person)
- 堆:存放new出来的对象和数组(分配具体的内存)(new Person())
Person person = new Person();
-
冒泡排序优化:定义一个flag=false,设置冒泡时flag=true,如果数组已排序,直接跳出
-
稀疏数组
二、面向对象
2.1简介
类是对象的抽象,对象是类的具体
- 本质:以类的方式组织代码,以对象的方式封装数据
- 三大特性:抽象(所有编程语言都有的特点,不算面向对象的特有的性质,从众多的事物中抽取出共同的、本质性的特征),三大特征都是为了抽象
-
封装
- 提高程序安全性,保护数据
- 隐藏代码的具体实现
- 提供接口访问
- 属性私有,提高了系统可维护性;在真实开发中,可以在set中限定一些不安全的情况(使用if判断)
-
继承:子类具有父类的全部特性(ctrl+H) 可查看当前类的继承关系,java只有单继承
- super()调用父类的构造函数,必须在第一行
- super()和this不能同时调用构造函数
- 重写,方法名和参数列表必须相同,返回值类型也必须相同,修饰符范围可以扩大,抛出的异常可以缩小
假如A继承了B
A a = new A();//a,b(变量),就叫引用,new的就叫对象 a.test(); B b = new A();//(相当于父类指向子类对象) b.test(); //如果test是静态方法(static修饰),a.test是子类的方法,b.test是父类的方法,如果test不是静态方法,则都是调用子类的方法 //如果子类有非重写的方法test1(),则b.test1()会报错(强制类型转化就可以)
-
多态:多态是方法的多态,属性没有多态。(属性是静态的数据,方法是动态的行为),多态存在的条件:
- 有继承关系
- 子类重写父类方法
- 父类指向子类对象:B b = new A();
- instanceof判断一个对象是否是某类的实例对象或者父类
- 把子类转成父类,向上转型,默认转换,可能会丢失一些方法(非父类的重写方法)
- 父类转成子类,向下转型,强制转换
-
修饰符
- public
- protected:
- 子类与基类在同一包中可访问
- 在不同包:子类实例可以访问其从基类继承而来的protected方法,而不能访问基类实例的protected方法。就是在父类包下实例化可以访问,在子类包中实例化不能访问(编译错误)
- default
- private
-
2.2代码块
- 静态代码块:只在类加载的时候执行一次,最先执行
- 匿名代码块和构造函数:实例对象的时候执行,匿名先执行
{
System.out.println("no name");//匿名代码块,一般赋初值
}
static {
System.out.println("static");//静态代码块
}
- 导入静态包,可直接使用包中的方法
import static java.lang.Math.random;
public static void main(String[] args) {
System.out.println(random());
}
- 静态变量会先分配地址空间设置为默认值,然后再看静态代码块和定义的静态变量上下位置赋初始值,按上下位置执行。
2.3 抽象类
- 抽象类的非抽象子类必须实现抽象类的所有抽象方法
- 不能被实例,只能靠子类实现
- 抽象方法(相当于一种规范)必须写在抽象类中,可以写普通的方法
- 抽象类有构造方法,接口没有
2.4. 接口
- 接口中所有方法都是public abstract,抽象的,比抽象类更抽象
- 接口中定义属性都是常量,public static final修饰
- 类只能单继承,而类可以实现多个接口,相当于伪多继承
- 接口中不能有构造函数
- 只有一个抽象方法的接口叫函数式接口,可使用Lambda表达式实现
public interface Abs {
public abstract void test(String message);
}
public static void main(String[] args) {
//Abs abs = () -> System.out.println("test");//无参
Abs abs = message -> System.out.println("test"+message);//有参
abs.test("dasd");
}
2.5 内部类
- 类里面再定义类,可以调用外部类的私有属性
Outer outer = new Outer();
Outer.Inner inner = outer.new Inner();
- 一个类可以定义多个类,但只能有一个public的类
- 方法类也可定义类,局部内部类
- 匿名内部类
public class test {
public static void main(String[] args) {
Service service = new Service() {//匿名内部类
@Override
public void hello() {
System.out.println("service");
}
};
}
}
interface Service{
void hello();
}
2.6 Error和Exception(快捷键Ctrl+alt+t)
- 检查性异常:用户错误使用或问题引起的,程序员无法预见的,如打开不存在的文件
- 运行时异常:可在程序编译时忽略,可以被程序员避免的异常,Exception一般程序可以处理,尽可能在程序中处理
- 错误:编译检查不到,如栈溢出,由JVM虚拟机生成抛出,是程序无法控制和检查的,产生了一般由JVM终止线程
- 自定义异常:直接继承Exception,然后实现构造函数和toString()方法就行
- try-with-resource机制:try(){}catch(){},将要关闭的外部资源在()中创建,catch()捕获处理异常。不用finally关闭资源
2.7 Date类
- 一般用Calendar类代替Date类
Calendar类是一个抽象类,且Calendar类的构造方法是protected的,API中提供了getInstance方法用来创建对象。 - SimpleDateFormat:用户可自定义日期的输出格式
public static void main(String args[]) {
Date now= new Date();
SimpleDateFormat ft = new SimpleDateFormat ("yyyy-MM-dd hh:mm:ss");
System.out.println("当前时间为: " + ft.format(now));
//当前时间为: 2018-09-06 10:16:34
}
2.8String类
- String不可变字符序列,少量字符串时使用
- StringBuffer:可变字符序列、效率低、线程安全
- StringBuilder:可变字符串,效率高、线程不安全
2.9 集合框架
Collection接口:储存可重复,无序
- List:接口,继承Collection,可重复,有序,查找效率高,插入效率低,可插入null
- ArrayList:常用,遍历提供更好的性能,非同步,多线程不要使用
- LinkedList:没有同步方法,多线程时,需要自己实现同步
- Set:接口,继承Collection,不可重复,无序,查找效率低,插入、删除效率高,可插入null
- HashSet:常用
- TreeSet:可实现排序
Map:接口,储存键值对
-
HashMap:它根据键的hashCode值存储数据,允许一个键为null,不支持线程同步;需要满足线程安全,可以用 Collections的synchronizedMap方法使HashMap具有线程安全的能力,或者使用ConcurrentHashMap。
-
Hashtable:Hashtable是遗留类,很多映射的常用功能与HashMap类似,不同的是它承自Dictionary类,并且是线程安全的,任一时间只有一个线程能写Hashtable,并发性不如ConcurrentHashMap,因为ConcurrentHashMap引入了分段锁。Hashtable不建议在新代码中使用,不需要线程安全的场合可以用HashMap替换,需要线程安全的场合可以用ConcurrentHashMap替换。
-
LinkedHashMap:LinkedHashMap是HashMap的一个子类,保存了记录的插入顺序,在用Iterator遍历LinkedHashMap时,先得到的记录肯定是先插入的,也可以在构造时带参数,按照访问次序排序。
-
TreeMap:TreeMap实现SortedMap接口,能够把它保存的记录根据键排序,默认是按键值的升序排序,也可以指定排序的比较器,当用Iterator遍历TreeMap时,得到的记录是排过序的。如果使用排序的映射,建议使用TreeMap。在使用TreeMap时,key必须实现Comparable接口或者在构造TreeMap传入自定义的Comparator,否则会在运行时抛出java.lang.ClassCastException类型的异常。
HashMap
- 使用哈希表来存储的。哈希表为解决冲突,可以采用开放地址法和链地址法、再哈希、建立公共溢出区,Java中HashMap采用了链地址法。
- HashMap初始长度为16,负载因子为0.75,最大容量为16*0.75,大于这个值就会开始扩容,一般为增加一倍,长度为2的n次方
- 从结构实现来讲,HashMap是数组+链表+红黑树(JDK1.8增加了红黑树部分)实现(产生Hash冲突(hash相等,key值不一样)就放在链表上,链表长度大于8自动转成黑红树,黑红树小于6自动转成表)
Collections
- 一个工具类,可实现集合的排序、基本查找、反转、复制和同步控制等功能
- 线程安全:Vector(list)、HashTable(map)(主要是因为实现同步耗费资源很大)
2.10 IO
- 字节流:大型文件用流处理,一般读取二进制,一个字节
- OutputStream
- InputStream
- 字符流:两个字节,一般读取字符
- Reader
- Writer
- 节点流:CharArray:可以从或向一个特定的地方(节点)读写数据
- 处理流:是对一个已存在的流(字符/字节)的连接和封装,通过所封装的流的功能调用实现数据读写。
- Buffer
- Data
- 转换流:InputStreamReader,OutputStreamWriter,字节转换为字符
- Filter
- 关键字:transient,设置透明,不会被序列化,节省空间
transient int modCount;
2.11 正则表达式
//判断字符串是否符合某个格式
public static void main(String[] args) {
String pattern="\\w{0,}\\@\\w{0,}\\.{1}\\w{0,}";//正则表达式(一个由一系列元字符组合成的字符串)
System.out.println("22222222@qq.com".matches(regularExpression));
}
- 泛形
- 默认使用方法
// 泛型方法 printArray,调用时只能使用引用数据类型(对象)
public < E > void printArray( E[] inputArray ){
// 输出数组元素
for ( E element : inputArray ){
System.out.printf( "%s ", element );
}
System.out.println();
}
- 限制类型
public static <T extends Comparable<T>> T maximum(T x, T y, T z){
T max = x; // 假设x是初始最大值
if ( y.compareTo( max ) > 0 ){
max = y; //y 更大
}
if ( z.compareTo( max ) > 0 ){
max = z; // 现在 z 更大
}
return max; // 返回最大对象
}
- 类型通配符:
?
代替具体的类型参数。例如List<?>
在逻辑上是List< String >,List< Integer > 等所有List<具体类型实参>的父类。
二、网络编程
协议
- 五层协议是把会话层及以上包括至应用层
- 简介
- 计算机网络
把不同地方的计算机通过协议连接起来。 - 网络编程的目的
数据交换、通信
- 入门
- 端口:表示计算机上的一个程序的进程
- TCP和UDP是两个不同的协议,每个协议都有65535个端口,TCP和UDP可同时使用80端口,单个协议下端口号不能冲突
- https端口:443 ,http:80
- 通信协议
-
TCP:连接需要
三次握手
四次挥手
,安全连接,面向连接最少需要三次,保证稳定连接 //三次挥手 A:你瞅啥? B:瞅你咋地? A:干一场! //四次挥手 A:发送一个数据,我需要断开连接,断开A->B数据传输 B:收到数据包,断开了B->A的数据传输 B:发送数据,表示我知道了,已断开数据传输 4.A:收到数据,断开TCP连接
四次挥手为何不是三次的原因是:三次挥手只能断开双方的数据传输,不能断开TCP连接
Windows默认编码是GBK,而不是UTF-8
-
UDP:不安全连接,面向数据报
三、多线程
状态
- 进程和线程的区别
- 进程:在操作系统中运行的程序,比如QQ、播放器、游戏、IDE
进程就是程序的一次执行过程,是资源分配的基本单位,一个进程包含多个线程 - 线程:比如看电影时:一个线程控制声音,一个线程控制图像,另一个控制字幕
真正执行的是线程,线程是CPU调度和执行的基本单位
注意:很多多线程都是模拟出来的,真正的多线程是指有多个CPU,即多核服务器;模拟出来的是在一个CPU的情况下,在同一个时间点只执行一个代码,由于切换快,就造成了多线程的错觉。
- main和gc
- main是用户线程,也是程序执行的主线程
- gc是守护线程,垃圾回收
- JVM会在所有的非守护线程(用户线程)执行完毕后退出;main不是最后一个退出的线程,所以main结束,jvm不一定结束
- 线程开启不会立即执行,由CPU调度
- 多线程实现
- 继承Thread:不建议
- 实现Runnable:需new一个Thread类去start
public class TestThread implements Runnable
TestThread testThread = new TestThread();
new Thread(testThread).start();
- 实现Callable:有返回值和能抛出异常
- Thread底层是静态代理实现的
- 真实对象和代理对象都要实现同一个接口
- 代理对象要代理真实对象
- 好处:可以做真实对象做不了的事,真实对象专注做自己的事情
public class StaticProxy {
public static void main(String[] args) {
new Thread(()-> System.out.println("结婚了")).start();//静态代理
new WeddingCompany(new You()).marry();
// WeddingCompany company = new WeddingCompany(new You());
// company.marry();
}
}
interface Marry{
void marry();
}
class You implements Marry{
@Override
public void marry() {
System.out.println("结婚了");
}
}
class WeddingCompany implements Marry{
private Marry target;
public WeddingCompany(Marry target) {
this.target = target;
}
@Override
public void marry() {
before();
this.target.marry();
after();
}
private void after() {
System.out.println("收彩礼");
}
private void before() {
System.out.println("布置现场");
}
}
- 线程停止
- 不推荐使用stop,destroy()方法,已废弃
- 推荐自己停下来,或建立一个标志位,当flag=false,线程停止
public class TestStop implements Runnable {
private boolean flag = true;
@Override
public void run() {
while (flag){
System.out.println("Thread--->Run");
}
}
public void stop(){
this.flag = false;
}
public static void main(String[] args) {
TestStop testStop = new TestStop();
new Thread(testStop).start();
for (int i = 0; i < 10; i++) {
if(i == 9){
testStop.stop();
}
}
}
}
- 线程休眠
- 每个对象都有一把锁,sleep方法不会释放锁,wait会释放锁
- sleep模拟网络延迟,方法并发问题的发生性
- 线程礼让 yield
- 让线程从运行态转成就绪态(start),让CPU重新调度线程线程,不一定成功
- 线程的状态
- 创建、就绪、运行、阻塞、死亡(死亡的线程就不能启动了)
- 优先级
- 优先级低只是意味着获得CPU调度的概率低
- 线程同步
- 加入锁机制synchronized,可能会引起性能倒置(高优先级的等待低优先级的释放锁,效率),比如上厕所,不是很急的先上,后面有个很急的等待
- synchronized:一般只同步修改操作,查询不用,默认同步this本身
同步需要同步修改的对象(增删改),查询不需要
//同步块,需在线程类内使用
synchronized(被修改的对象){
//具体代码
}
- 死锁
- 两个线程抢一个唯一资源(一个同步块拥有两个对象锁)
if (choice == 0){
synchronized (lipStick){
System.out.println(this.girlName+"获得口红");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (mirror){
System.out.println(this.girlName+"获得镜子");
}
}
}else {
synchronized (mirror){
System.out.println(this.girlName+"获得镜子");
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (lipStick){
System.out.println(this.girlName+"获得口红");
}
}
}
//将里面的同步块拿出来就好了,不要让一个代码块获取两个对象锁
- 死锁的条件
- 互斥:一个资源一次只能一个线程使用
- 请求与保持:一个进程因请求资源阻塞,对已有的资源保持不放
- 不可剥夺:进程获得的资源,在使用完之前,不能被剥夺
- 循环等待:若干进程形成头尾相接的循环等待关系
- Lock
- 锁提供了对共享资源的独占访问,每次只能一个线程对Lock对象加锁
- 一般使用ReentrantLock(可重入锁)
try {
lock.lock();//加锁
if(num >= 0){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(num--);
}else{
break;
}
} finally {
lock.unlock();//解锁
}
synchronized和lock的区别:
- lock是显式锁、synchronized是隐式,出了作用域自动释放
- lock只能锁代码块,s…方法也可
- lock锁,jvm花费时间少,性能好
- 优先使用顺序:Lock>同步代码块>同步方法
- 生产者消费者模式
- 使用synchronized同步,wait和notify/notifyAll实现线程通信(wait会让出对象锁,sleep不会),相当于wait在syn代码块中不需要执行完才让出对象锁
- 但notify只是唤醒wait的线程,但是不会释放对象锁,需要等当前syn执行完才会释放,另一个wait的线程才会进入等待状态,等待CPU调度
- 线程池
- 提前创建多个线程,使用时在池中获取,使用完放回,避免频繁创建销毁造成的性能开销,实现重复利用,提高性能
- 好处:提高响应速度,降低资源消耗,便于线程管理
四、注解和反射
- 内置注解
- @Deprecated:表示不鼓励程序员用这样的元素,如废弃方法
- @SupperessWarnings:抑制编译器错误信息,镇压警告
- 元注解(meta-annotation):解释其他注解的注解
- @Target:描述使用范围,方法上还是类上
- @Retention:表示在什么地方注解还有效,源代码,编译,运行时(SOURCE<CLASS<RUNTIME(一般用))
- @Document:将注解生成到JavaDoc中
- @Inherited:子类可以继承父类的注解
public class TestAnnotation {
@MyAnnotation(age = 1)
public void test(){
}
}
@Target({ElementType.METHOD,ElementType.TYPE}) //可在哪使用
@Retention(RetentionPolicy.RUNTIME) //定义在什么生命周期生效
@interface MyAnnotation{
//参数:类型+名字 +() + 可选(default n 默认值)
String name() default "";
int age();
String[] sName() default {"成信大","川大"};
}
- 反射
- 正常:引入包名->new 实例化->取得实例对象
- 反射:实例化对象->getClass()方法->得到完整的包名
Class c1 = Class.forName("com.lvboaa.User");
- 一个Class对象加载到JVM中只会有一个
类加载的过程:
- 加载:将class文件通过javac编译加载到内存中,将静态数据转化成方法区的运行时数据,生成一个class对象
- 链接:将二进制代码合并到JVM Java的运行环境中
- 验证:确保类信息符合JVM规范,没有安全问题
- 准备:为类变量(static)分配内存赋初始值,并将之放入常量池
- 解析:将引用类型替换成真实的地址
- 初始化
- 执行类构造器方法,合并静态代码块(包括块和变量),合并的时候跟静态变量和代码块的前后顺序有关
- 初始化一个类,如果发现父类没初始化,就先初始化父类
- 虚拟机保证方法在多线程安全(同步)
调用类的常量和静态变量、新建对象数组不会对类进行初始化
- 反射创建对象
public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException,
InstantiationException, NoSuchMethodException, InvocationTargetException, NoSuchFieldException {
//获取Class对象
Class c1 = Class.forName("com.lvboaa.test1.User");
User user = (User) c1.newInstance(); //创建对象,本质是调用了无参构造
System.out.println(user);
//通过构造器创建
//参数需要和用什么构造器创建对象的参数一致
Constructor constructor=c1.getDeclaredConstructor(int.class,String.class);
User user1 = (User) constructor.newInstance(1,"lvbo");//设置参数
System.out.println(user1);
//通过反射调用普通方法
User user2 = (User)c1.newInstance();
Method method =c1.getDeclaredMethod("setName",String.class);
method.invoke(user2,"lvbo");
System.out.println(user2);
//通过反射访问属性
User user3 = (User)c1.newInstance();
Field name = c1.getDeclaredField("name");
name.setAccessible(true);//设置安全检查关闭,就可访问私有属性
name.set(user3,"lvbo");
System.out.println(user3);
}
反射就是在运行时才知道要操作的类是什么,并且可以在运行时获取类的完整构造,并调用对应的方法。
效率问题:直接new对象调用方法速度最快,反射慢了几百倍,关闭检测也比普通反射效率快了三倍左右,所以需要经常调用反射一般关闭检测(不允许访问private修饰的…)。