目录
JAVA基础知识全面复习
第一章
编写代码、编译代码、运行代码
class :定义一个类 ,后面跟上helloworld
程序执行时的入口点,main方法也是主方法
踩坑:
运行JAVA程序后出现找不到或无法加载主类
经排查发现是权限不够 ,以管理员身份运行JAVA程序
常见面试题:
JDK、JVM、JRE的区别
1.JVM:java虚拟机,真正JAVA程序运行的地方
JRE:JAVA运行环境 JRE 包括 JVM+核心类库 核心类库:JAVA自己写好的程序
JDK:JAVA开发工具包(包括上面所有) JRE+开发工具
PATH环境变量:记住程序路径
JAVA_HOME:告诉操作系统JDK安装在了那个位置
IDEA基本结构
Project-module-package-class
快捷键
JAVA基本语法
变量:内存中的一块区域用来存储数据,并且数据可以改变 变量要先申明后使用,申明后不可以更改数据 不可重名 定义可以不初始化 使用必须初始化
关键字:JAVA语言自己用到的一些字 ;类名或者变量名称时,不能使用这些词
标识符:方法名、变量名、类名 基本要求:由数字、字母、下划线、$组成
不能以数字开头、不能是关键字、区分大小写
变量名称:全英文 首字母小写 驼峰命名
类名称:首字母大写 驼峰命名
第二章
数据类型是约束变量存储数据的形式
基本数据类型:
整数 byte short int(默认) long
浮点数 float double (默认)
字符 char
布尔 boolean
引用数据类型
自动类型转化 小——大
强制类型转化 大-------小 类型范围大的数据不能直接赋值给类型范围小的数据
注意事项: 强制类型转换可能造成数据溢出
浮点型 强转成整形 直接截断小数
运算符:算术运算符、自增自减运算符、赋值运算符、关系运算符、逻辑运算符、三元运算符、运算符优先级
算术运算符:+ - * / %
+做连接符: 能算则算,不能算就在一起
自增自减运算符
++前:先运算再使用 ++后: 先使用后运算
赋值运算符: =、+=、-=、*=、/=、%=
关系运算符:==、!=、>、>=、<、<=
逻辑运算符:&、|、! ^(异或)
短路与 && ||
三元运算符: 条件表达式 ?值1:值2
运算符:*/高于+-
程序控制:顺序结构(程序默认流程)、分支结构(判断条件选择程序执行)、循环结构(重复执行某段程序)
分支结构:if switch
switch: 表达式只能是byte short int char String 不支持 double float long
case给出的值不允许重复,并且只能是字面量、不能是变量
不要忘记写break 否则会出现穿透
三种循环的区别 for循环和while循环 (先判断后执行)
do ....while(先执行后判断)
break :跳出并结束当前所在循环
continue:跳出当前循环
Random
作用:用于在程序中获取随机数的技术
nextInt(n):生成0~n-1的随机数,不包含n
第三章
数组
数组就是用于存储一批同种类型数据的容器
静态初始化数组:
数据类型[] 数据名 ={元素1,元素2,元素3........}
数组是引用类型,数据变量名是存储在内存中的地址
数组中的动态初始化
int [] arry=new int[3]; 确定长度
区别:当前已经知道存入的元素值.用静态初始化
当前不知道数据.用动态初始化 不可以混用
JAVA内存分配
栈、推、方法区、本地方法栈、寄存器
方法区:字节码文件加载时进入内存
栈内存:方法运行时所进入的内存 变量就在这里
堆内存:new 出来的东西会在这块内存中开辟空间并产生地址
基本类型变量 存储的时变量本身 引用数据类型 存储的时所指向的地址
超过最大索引 ArrayIndexOutofBoundsException
数据变量中没有存储数组的地址 访问数组信息NULLPointerException
第四章
方法
方法修饰符 返回值类型 方法名称 形参列表
public static add (int a,int b)
方法的内存图
方法没有被体哦啊用的时候 ,在方法区中的字节码文件中存放
方法被调用 .需要在栈内存中运行
方法的参数传递机制:
在传输实参给方法形参时,并不是传输实参变量本身 ,而是传输实参变量中存储的值,这就是值传递
形参:以方法为例 就是方法定义时的变量
实参:在方法内部定义的变量
值传递:传输的是实参存储的值
基本类型和引用类型的参数在传递的时候的区别
都是值传递
基本类型的参数传输存储的是数据值
引用类型的参数传输存储的地址值
方法的重载与重写:
重载:同一个类中,出现多个方法名称相同 ,但是形参列表不同,那么这些方法是重载方法(忽视修饰符、返回值类型)
方法的重写:子类对父类的允许访问的方法的实现过程进行重新编写
双色球案例
注意:随机生成中奖号码时应该时每个红球号码都不重复
OOP
对象在内存中的运行机制
栈内存: Car c1放置引用变量地址 堆内存: new 出来的对象 放置对象成员方法的引用地址
调用方法的过程:
1、c1所指向的对象
2、方法的引用找到start方法
3、在推内存中运行start方法
垃圾回收
垃圾对象:堆内存的对象无任何引用指向该对象 定期回收
构造器: 定义在类中,可以初始化一个类的对象,并返回对象的地址
无参、有参
this:
可以出现在构造器、方法中 代表当前对象的地址
封装:
如何正确设计对象的属性和方法
面向对象的三大特征:封装、继承、多态
一般建议把成员变量使用 private隐藏起来,不对外访问
提供public修饰的getter和setter方法暴露其取值和赋值
javaBean:实体类,其对象可以用于在程序中封装数据
成员变量和局部变量的区别:
区别 成员变量 局部变量
类中的位置 类中方法外 方法内
初始化值 有默认值 无默认值
内存位置 堆 栈
生命周期 随对象 随方法
作用域 所属大括号
常见API
String和ArrayList
String类定义的变量可以用于指向字符串对象 然后操作字符串 不可变字符串类型 对象在创建后不可以修改
字符串对象在字符串的常量池中
只是引用变量指向新的对象的地址
equals方法默认==字符串中重写该方法 只关心内容是否一致
常见 API
length()、charAt 、toCharArray、substring replace
eg: String s1=new String ("abc"); 两个对象
String s2="abc";0个对象
s1==s2 false
eg: 只要不是双引号直接给出不放到字符串常量池
eg: String s1="abc"
String s2="a"+"b"+"c"
s1==s2 true 字符串常量池的数据共享
编译优化机制,在编译自动转成 abc
ArrayList集合
常见 API
add 泛型:只支持引用类型 编译阶段约束集合对象只能操作某种数据类型
get size remove set修改指定位置的元素
集合中存储的时堆内存的地址
第五章
static关键字: 设计模式:单例模式:有些类只需要一个对象就可以了,如何实现一个类只能对外产生一个对象
static静态:可以修饰成员变量、成员方法
static修饰成员变量之后称为静态成员变量(类变量),修饰方法之后称为静态方法(类方法)
static修饰后的成员变量,可以被类的所有对象共享(访问、修饰)
成员变量内存原理
static:静态的意思,可以修饰成员变量、成员方法 静态成员变量(static修饰,属于类、加载一次,内存中只有一份)
实例成员变量(无static修饰,属于对象)
两种成员变量:静态成员变量:表示在线人数等需要被类的所有对象共享的的信息
实例成员变量;属于每个对象,且每个对象的该信息不同
静态成员方法:(有static修饰,归属于类):建议用类名访问,也可以用对象访问
实例成员方法:(无static修饰,归属于对象) 只有对象触发访问
静态成员方法(有static修饰,属于对象和类共享):类名。静态成员方法 对象.静态成员方法
实例成员方法:(无static修饰,属于对象) 对象.实例方法
static注意事项:
静态方法只能访问静态成员,不可以直接访问实例成员
实例方法可以访问静态成员,也可以访问实例成员
静态方法是不可以用this关键字
工具类:工具里面都是静态方法,直接类名即可访问 工具类无需创建对象 ,建议将工具类的构造器进行私有
代码块:类中的五大成分(成员变量、构造器、方法、代码块、内部类)
静态代码块:初始化静态资源
设计模式
开发中经常遇到一些问题 ,一个问题通常有n种揭解法,但其中肯定有一种解法最优 ,称为设计模式
单例模式 应用该模式的这个类永远只有一个实例,即一个类永远只能创建一个对象
饿汉单例 : 在用类获取对象的时候,对象已经提前为你创建好了
懒汉单例: 在真正需要该对象的时候,才去创建对象(延迟加载对象)
//1.定义一个类,把构造器私有 定义一个静态变量存储一个对象 提供一个返回单例对象的方法
继承
继承就是JAVA运行我们用extends 关键字 让一个类和另外一个类建立父子关系
提高代码的复用性 减少代码冗余
父类:共性 子类:特有
内存空间中
子类空间:
父类空间:
子类可以继承父类的属性和行为,但是子类不能继承父类的构造器
JAVA是单继承模式,一个类只能继承一个直接父类 JAVA不支持多继承 JAVA中所有的类都是Object类的子类
子类方法中访问成员:就近原则,子类没有找子类 子类没有找父类 父类没有保错
出现重名:super.父类成员变量 私有方法不能被重写 子类重写父类方法时,访问权限大于或等于父类被重写的权限
子类初始化,一定
子类构造器中:默认第一行语句:super() 不写也存在
子类中所有构造器默认都是先访问父类中无参的构造器,再执行自己
this和super使用的注意点
子类通过 this(...)去调用本类的其他构造器 ,本类其他构造器会通过super去手动调用父类构造器
this(...) super(...)都只能放在构造器的第一行 所以二者不能存在同一个构造器
包: 修饰符 同一个类 同一个包中的其他类 不同包下的子类 不同包下的无关类
private √
default √ √
protected√ √ √
public √ √ √ √
final关键字:final关键字时最终的意思 可以修饰(类、方法、变量)
修饰类:表明该类是最终类 不能被继承
修饰方法: 表明该方法是最终方法 不能被重写
修饰变量:表示该变量第一次赋值后,不能再次被赋值(有且仅能被赋值一次)
final 注意:final修饰的变量是基本类型:变量存储的数据值不能发生改变 final修饰的变量是引用类型:变量存储的地址值不能发生改变,但是地址指向的对象内容是可以发生改变
public static final修饰的成员变量 必须有初始值,而且执行的过程不能改变
枚举是JAVA中的一种特殊类型 枚举的作用:为了做信息的标志和信息的分类
抽象类:
抽象方法中只有方法签名,不能声明方法体
一个类如果定义了抽象方法,这个类必须声明成抽象类
抽象类中不一定有抽象方法,有抽象方法的类一定是抽象类
abstract:得到了抽象方法,不能创建对象
多态
多态:指对象可以有多种形态
前提:有继承关系 父类引用指向子类对象 方法重写
自动转化 子到父 强制转化 父到子 instanceof(判断当前对象的真实类型)
接口:
类和类的关系:单继承
类和接口的关系:多实现
接口和接口的关系:多继承,一个接口可以同时继承多个接口
JDK8开始新增的方法
默认方法:默认会public修饰。需要用接口的实现类的对象来调用
静态方法:static修饰,必须用当前接口名调用
私有方法:private修饰 ,jdk9开始只能在接口内部被调用
默认public
内部类:定义在一个类里面,里面的类可以理解成寄生,外部类可以理解成宿主
访问外部内对象 外部类名.this
匿名内部类:
本质上是一个没有名字的局部内部类 作用:方便创建子类对象 最终目的是为了简化 编写
特点总结:匿名内部类是一个没有名字的内部类,同时也代表一个对象
匿名内部类产生的对象类型,相当于是当前new的那个的类型的子类类型
Object的equals方法: equals方法默认是比较当前对象与另外一个对象的地址是否相同,相同返回true 不同 false
Objects类的toString equals方法
StringBuilder的核心作用:操作字符串的性能比string要更高
stringBuilder:内容是可变的、拼接字符串性能好、
String:内容不可变 拼接字符串性能差
BigDecimal
Date 类: Date类代表当前所在系统的日期时间信息
日期对象创建: public Date(); public long getTime();
恢复时间: public Date(long time); public void setTime(long time);
简化日期格式化:SimpleDateFormat 构造器 public SimpleDateFormat(String pattern) 创建简单日期格式化对象
格式化方法 format(Date date) 将日期格式化成日期 format(Object time)将时间毫秒值化成日期
parse(String source):解析字符串时间
新的日期API
localDate:不包含具体时间的日期 LocalTIme:不包含日期的时间
LocalDateTIme:包含了日期及时间
Instant 代表时间戳
Arrays类:数组操作工具类
toString(类型 [] a) 返回数组的内容
Comparator 重写 compare方
Lambda表达式只是简化函数式接口的匿名内部类的写法形式
函数接口中有@FunctionallInterdface
Lambda表达式的规则: 参数类型可以不写
如果只有一个参数 ()也可以省
lambda 只有一行 可以省 大括号 省分号 若是return语句 必须省return 同时也必须省;
集合{
Collection:单列
Map :双列
}
Collection集合
迭代器: Iterator
lists.foreach 结合lambda表达式
数据结构 栈、队列、数组、链表、二叉树、二叉查找树、平衡二叉树、红黑树
二叉树:父节点、值、左子节点地址、右子节点地址
只有一个根节点:
节点的度节点拥有的子树的个数 高度:叶子节点为1 层:根节点y在第一层 兄弟节点:有公共父节点的节点叫叶子节点
二叉查找树 :
平衡二叉树:满足二叉查找树的基础上数据分布均匀 提高二叉树数据的性能**
任意节点的左右两个子树的高度差不超过1,任意节点的左右两个子树是一颗平衡二叉树
平衡二叉树:左旋、右旋 左左 右旋 左右1.先左旋 成左左再右旋 右右 往左啦 右左 先右拉再左拉 (左子树 的左节点 左子树的右节点 )
TODO 红黑树:平衡二叉B树 平衡二叉查找树
ArrayList底层原理:ArrayList底层是基于数组实现的,根据索引定位元素快 增删需要左元素移位操作
默认为10
LinkedList 底层数据结构是双链表,查询慢 ,首尾操作极快,
stream流
异常:Throwable
error exception
RuntimeException 除RuntimeException之外的异常
常见的异常:
数组索引越界异常:ArrayIndexOutOfBoundsException
空指针异常: NullPointerException
数学操作异常:ArithmeticException
类型转换异常: ClassCastException
数字转换异常 NumberFormatException
编译时异常的处理形式:出现 异常直接抛出去给调用者 ,调用者也继续抛出去
出现异常自己捕获处理,不麻烦别人
异常的默认处理机制:默认会在出现异常的代码的创建一给异常对象
异常会从方法出现的点这里直接抛出给调用者 调用者最终抛给JVM虚拟机
虚拟机接收到异常对象后,先在控制台直接输出异常栈信息数据
直接从当前执行的异常点干掉程序
后续代码无机会执行
-
throw 在方法体内使用,throws 函数名后或者参数列表后方法体前
-
意义 : throw 强调动作,而throws 表示一种倾向、可能但不一定实际发生
-
throws 后面跟的是异常类,可以一个,可以多个,多个用逗号隔开。throw 后跟的是异常对象。
自定义异常
//自定义异常
1.自定义异常类,继承Exception
2、自定义构造方法,无参、带参都可 传入异常信息
3、上抛 throws异常
异常是一个非常好用的工具,可以在不中断程序的同时发现问题,所以这个方法一定要掌握 异常分运行时异常和编译时异常,要知道如何区分,编译时异常在写代码的时候编译器就会报错,运行时异常在程序运行时才会报错 throws 抛异常,抛得最远只能到main方法, 如果再继续抛,到jvm虚拟机,它不会处理异常,会报错 手写异常,一般都要把异常抛出去,不然自己写的异常马上又处理 了,没有意义 try catch 是直接把异常处理掉了,throws 是向上抛异常,也是一种异常的处理方式throws 抛异常,抛得最远只能到main方法, 如果再继续抛,到jvm虚拟机,它不会处理异常,会报错 手写异常,一般都要把异常抛出去,不然自己写的异常马上又处理 了,没有意义 try catch 是直接把异常处理掉了,throws 是向上抛异常,也是一种异常的处理方式
**throw
用于抛出一个已知的异常,而 throws
用于声明可能会抛出的异常。**
throws: 可以跟多个异常类名,用逗号隔开 throw: 只能抛出一个异常对象名
throws: 表示抛出异常,由该方法的调用者来处理 throw: 表示抛出异常,由该方法体内的语句来处理
throws: throws表示有出现异常的可能性,并不一定出现这些异常 throw: throw则是抛出了异常,执行throw一定出现了某种异常
使用 throw
时,必须指定异常对象
设计模式 工厂设计模式 提供获取对象的方式
装饰模式:在不改变原来基础上动态增加功能
线程池的三种方式的对比:
继承Thread类
实现 Runnable接口 实现Callable接口
TODO IO流 多线程 红黑树
FILE类和IO流
数据希望以文件的形式存储 1、围绕文件的操作 2、读写数据
File类的对象代表操作系统中的文件、文件夹 File类提供了诸如:创建文件对象代表文件、获取文件信息(大小、修改时间) 删除文件、创建文件
pathname String 绝对路径是带盘符 相对路径不带盘符
常见方法:isDirectory() isFile() exists () length() getAbsolutePath() getPath() getName() lastModified()
创建文件、删除文件; createNewFile mkdir mkdirs delete
listFiles() 获取当前目录下所有的“一级文件对象”到文件对象数组中去返回
注:listFiles只能返回一级文件对象
delete方法默认只能删除文件和空文件夹 ,delete直接删除不走回收站
递归算法的三要素:
递归公式:f(n)=f(n-1)*n 递归的终结点 f(1) 递归的方向走向终结点
文件搜索:先定位出的是一级文件对象 遍历全部的以及我文件对象 判断是否是文件 如果是文件 判断是否是自己想要的 如果是文件夹继续递归
字符集: ASCII:包含数字、英文、符号
GBK:包含了几万个汉字等字符
英文和数字等在任何国家的字符集都占1个字节 GBK字符中一个中文字符占2个字节 UTF-8中一个中文占3个字节
字符集编码和解码需要一致,否则会乱码
字符集的编码、解码操作
String编码; getBytes() getBytes(String charsetName):指定编码 解码 String(byte[] bytes ,String charsetName)
IO流: I表示input,输入的过程,负责读 O表示output 把内存的数据写出到硬盘文件的过程,称为输出,负责写
IO流:按流中的数据最小单位分为字节流、字符流
操作所有类型的文件、只能操作纯文本文件、
字符流:Reader Writer FileReader FileWriter
字节流:inputStream OutputStream FileInputStream FileOutPutStream
FileInputStream fileInputStream=new FileInputStream(file); System.out.println(fileInputStream.read());
以字节的形式进行读取 每次读取第一字节 字节数组
定义一个字节数组与文件的大小一样大 一次性读取完成 字节输入流InputStream
FileOutputStream:以内存为基准 ,把内存中的数据以字节的形式写到磁盘文件中的流
flush():刷新流,还可以继续写数据 close():关闭流,释放资源
字符流:字符流-一次读取一个字符 字符输入流-依次读取一个字符数组 字符输出流
字节缓冲输入流自带了8KB缓冲流,以后直接从缓冲池中读取数据
缓冲流自带缓冲区 :缓冲流也称为高效流 作用:缓冲流
缓冲流自带缓冲区,可以提高原始字节流、字节流读写数据的性能
字节缓冲流输入流:BufferedInputStream BufferedOutputStream 字符缓冲输入流:BufferedReader BufferedWriter
转换流:InputStreamReader OutputStreamWrite将字节流转换为字符流
对象序列化:以内存为基准,把内存中的对象存储到磁盘文件中去,称为对象序列化 使用的流是对象字节输出流 ObjectOutputStream 把低级字节输出流包装成高级的对象字节输出流 ObjectOutputStream把对象写出去到对象序列化流的文件中去
注:JAVA对象序列化乱码正常,(若不想为乱码)需要自己实现序列化
PrintStream PrintWriter
l打印数据功能上是一模一样的,都是使用方便,性能高效(核心优势)
lPrintStream继承自字节输出流OutputStream,支持写字节数据的方法。
lPrintWriter继承自字符输出流Writer,支持写字符数据出去。
打印流一般是指:PrintStream PrintWriter
PrintStream继承自字节输出流OutputStream PrintWrite 继承字符输出流Writer
第五章
多线程
多线程的创建的三种方法:
继承Thread类,重写run()方法
只有调用start方法才是启动一个新的线程
方法一:1.继承Thread类 2.重写run 3.创建线程对象 4.调用start方法
方法二:1.实现Runable接口 ,重写run()方法 2.创建MyRunnable对象 3.将MyRunnable对象交给Thread类处理 4.调用thread类的start()
方案二.2:可以创建Runnable接口
方案三:利用Callable、FutureTask接口实现
1.创建类实现Callable接口
2.创建线程任务对象 FutureTask把Callable对象封装成线程对象
3.创建thread类接受线程任务对象
4.调用start方法 利用futureTask放回结果
Thread getName()获取当前线程名称 setName 设置线程名称 currentThread返回当前正在执行的线程对象的引用 run 线程任务方法 start 线程启动方法
线程安全问题发生的原因:多个线程同时访问同一个共享资源并且存在修改该资源
解决方法同步代码块: synchroized
作用:把发现线程安全问题的核心代码给上锁 原理:每次只能一个线程进入,执行完毕解锁
同步方法:把出现线程安全的核心方法给上锁 synchronized
ctrl+alt+t键快捷键
锁对象任意的唯一:不好,影响其他线程的执行
规范:建议使用共享资源作为锁对象 对于实例方法建议使用this作为锁对象 静态方法建议使用 字节码.class
同步方法: syschronized 层可瑞子的
同步方法其实底层也是隐式锁对象
Lock锁 罗格锁
Lock上锁和解锁更加灵活 :Lock接口 ReentrantLock来构建Lock锁对象
Lock的API ReenttrantLock 瑞吹歌罗格
线程池
线程池就是一个可以复用线程的技术
如果用户发送一个请求 后台就创建一个线程来处理,下次新任务来了又要创建新线程 ,而创建新线程的开销比较大
工作线程:WorkThread WorkQueue
代表线程池的接口:ExcutorService e可四service Tool
两种: ExecutorService ThreadPoolExecutor EXecutors(线程池的工具类)
corePoolSize maximumPool keepAliveTime unit workQueue threadFactory handler
指定线程池的线程数量 指定线程池可支持的最大线程数 指定线程池的最大存活时间 指定存活时间的最大
临时线程什么时候创建:核心线程都在忙 任务队列满 并且还可以创建临时线程 创建临时线程
pool shutdownNow()立即关闭 即使任务没有完成,会掉任务 shutdown()热舞执行完成后关闭
Runnable对象 Callable对象 submit提交
Excutors得到线程类的工具类通过调用方法返回不用类型的线程类对象
newCachedThreadPool() newFixedThreadPool
ExecutorService newFixedThreadPool(int nThreads)
定时器:
方式一:Timer 方式二:ScheduledExecutorService s可to的
定时器是一种控制任务延时调用,或者周期调用的技术
线程的六种状态
网络编程关键三要素:IP地址、端口、协议
IPV4(32bit)四字节 IPV6(128bit)16个字节 IPV6分成8个整数,每个整数用四个16进制表示 数之间用:分开
IP地址形式: 公网地址和私有地址
InetAddress: getLocalHost(获取当地) getByName getHostName getHostAddress isReachable(IP地址是否互通)
192.168开头的就是常用的局域网地址,范围为192.168.0.0-192.168.255.255 专门为组织机构内部使用
端口号:标识正在计算机设备上运行的进程,被规定为一个16位的二进制
端口类型:周知端口(0-1023)被某些知名应用占用、、 注册端口(1024-49151) 动态端口(49152-65535)
传输层的2个常见协议:传输控制协议 用户数据报协议 TCP协议特点:
TCP协议,必须双方先建立连接 它是一种面向连接的可靠通信的协议 传输前 采用 三次握手的方式建立连接,所以是可靠的 在连接中可进行大数据量的传输 来凝结、发送数据都需要确认,且传输完毕后,还需是释放已建立的连接
TCP三次握手、四次挥手:
三次握手: 客服端向服务器发出请求 服务器给客服端返回响应信息、客服端再次发出确认请求
四次挥手:客服端向服务器发出取消请求的连接 、服务器向客服端发出一个响应、服务器向客服端发出确认取消信息、客服端再次发送确认信息
UDP协议:UDP是一种无连接、不可靠传输的协议 UDP协议通信场景:语音通话、视屏会议
UDP通信实现:发送消息、接受消息 1.创建DatagramSocket(data歌瑞缩雷特)对象(发送端对象) DatagramPacket对象封装需要发送的数据 使用DatagramSocket对象的send方法传入DatagramPacket对象
UDP快速入门; 发送端:DatagramSocket(人) DagegramPacket()韭菜盘子 里面装有数据 指定服务器端的端口号 人发送韭菜盘子中的数据
接受端: DatagramSocket(人)指定服务器端端口号 创建韭菜盘子 接受数据 打印数据
send发送端的数据 receive接受数据
UDP的三种通信方式: 单播:单台主机与单台主机之间的通信
广播 :当前主句与所在网络中的所有主机的通信 组播:当前主机与选定的一组主机的通信
使用广播地址:255.255.255.255
DatagramSocketd的子类 MulticastSocket可以在接收端绑定组播IP
TCP通信模式
TCP是一种面向连接、安全、可靠传输数据的协议 传输前.采用“三次握手”方式,点对点通信,是可靠的
传输前,采用“三次握手”方式,点对点通信,是可靠的
在连接中可进行大数据量的传输
TCP多发多收 不可以接受多个客服端的消息
TCP可以处理多个客服端的通信
引入多线程、
线程池:服务端可以复用处理多个客户端 可以避免系统崩溃
//线程池 工作线程 最大线程 任务队列
线程池的优势:服务端可以复用线程处理多个客服端 适合客户端同行时长较短的场景
即使通信 点对点 ,一个客户端的消息 另外一个客户端接受
即使通信:端口转发
BS结构:socket
Junit框架
单元测试
怎样进行预期结果的正确性测试:断言 assertEquals 返回值
@Before @after @BeforeClass 修饰静态方法
反射:
反射是指对于任何一个class类 在运行的时候都可以直接得到这个类的全部成分
这种运行时动态获取类信息以及动态调用类中的
得到这个类的构造器对象 Constructor Field Method class类对象
反射是在运行时获取类的字节码文件对象
class作为一个类型
得到Class类的方法 Class类的forName
类名.class
对象.getClass();
反射:第一步获得 class对象 获得Constructor对象 创建对象
getConstructor getDeclaredConstructor(无所谓权限)
使用反射技术获取构造器对象并使用 newInstance() 根据指定的构造器创建对象
暴力反射 setAccessible 权限被全开 反射破坏封装性
利用反射技术获取构造器对象的方式 : getDelaredConstructors()
反射获取成员变量
Filed类 set get
getDeclaredMethod:获取方法的执行的参数
Method类用于触发执行的方法 invoke(对象) 返回结果回来 那么返回的是null 关系反转
反射是作用在运行时的技术,此时集合的泛型将不能产生约束 此时可以为集合存入其他任意类型的元素的
反射可以绕过编译阶段
反射做通用框架:
注解的作用:对JAVA中类、方法、成员变量做标记,然后进行特殊处理
只有单个value可以不写
元注解: 注解的注解 @Target:约束自定义注解只能在哪些地方使用 @Retention:申明注解的生命周期
注解解析
Annotation
工厂模式:对象通过工厂的方法创建返回,工厂的方法可以为该对象进行加工和数据注入
可以实现类与类的解耦
装饰模式:在不改变原有的类的功能形况下对类的功能进行增强
动态代理就是,在程序运行期,创建目标对象的代理对象,并对目标对象中的方法进行功能性增强的一种技术
根据我们在动态代理对象中的“指示”,动态生成的。也就是说,你想获取哪个对象的代理,动态代理就会为你动态的生成这个对象的代理对象
-
首先由Proxy类的静态方法newProxyInstance创建动态代理对象
-
public static Object newProxyInstance
-
(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h)
-
而要创建该对象,需要3个参数
-
ClassLoader loader:定义了由哪个ClassLoader对象来对生成的代理对象进行加载
-
Class<?>[] interfaces:表示的是我将要给我需要代理的对象提供一组什么接口
-
InvocationHandler h:既然一个指明了加载器,一个指明了接口,那么这个就是具体实现功能的方法了。
-
InvocationHandler h:既然一个指明了加载器,一个指明了接口,那么这个就是具体实现功能的方法了。
-
InvocationHandler对象,表示的是当我这个动态代理对象在调用方法的时候,会关联到哪一个InvocationHandler对象上
-
每一个动态代理类都必须要实现InvocationHandler这个接口,当我们通过代理对象调用一个方法的时候,
-
这个方法的调用就会被转发为由InvocationHandler这个接口的invoke 方法来进行调用
-
public Object invoke(Object proxy, Method method, Object[] args)
-
InvocationHandler接口中invoke方法的三个参数:
-
proxy:代表动态代理对象
-
method:代表正在执行的方法
-
args:代表调用目标方法时传入的实参
动态代理就是在程序运行期,创建目标对象的代理对象,并对目标对象中的方法进行功能性增强的一种技术。在生成代理对象的过程中,目标对象不变,代理对象中的方法是目标对象方法的增强方法,可以理解为运行期间,对象方法的动态拦截,在拦截方法前后执行功能操作
代理类在程序运行期间,创建的代理对象称之为动他代理对象,这种情况下,创建的代理对象,并不是事先在JAVA代码中定义好的,而是在运行期间,更具我们在动态代理对象中的指示,动态生成的,也就是说,你想获取那歌对象的代理.动态代理就会为你动态的生成这个对象的代理对象,动态代理可以对别代理对象的放大进行功能增强。
有了动态代理的技术,那么就可以在不修改方法的源码的情况下,增强被代理对象的功能,在方执行前后做任何你想做的事
-
基于接口的动态代理
-
提供者:JDK
-
使用JDK官方的Proxy类创建代理对象
-
注意:代理的目标对象必须实现接口
-
-
基于类的动态代理
-
提供者:第三方 CGLib
-
使用CGLib的Enhancer类创建代理对象
-
注意:如果报 asmxxxx 异常,需要导入 asm.jar包
-
public class LogProxy { /** * 生成对象的代理对象,对被代理对象进行所有方法日志增强 * 参数:原始对象 * 返回值:被代理的对象 * JDK 动态代理 * 基于接口的动态代理 * 被代理类必须实现接口 * JDK提供的 */ public static Object getObject(final Object obj){ /** * 创建对象的代理对象 * 参数一:类加载器 * 参数二:对象的接口 * 参数三:调用处理器,代理对象中的方法被调用,都会在执行方法。对所有被代理对象的方法进行拦截 */ Object proxyInstance = Proxy.newProxyInstance(obj.getClass().getClassLoader() , obj.getClass().getInterfaces(), new InvocationHandler() { public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { //方法执行前 long startTime = System.currentTimeMillis();
Object result = method.invoke(obj, args);//执行方法的调用 //方法执行后 long endTime = System.currentTimeMillis(); SimpleDateFormat sdf = new SimpleDateFormat(); System.out.printf(String.format("%s方法执行结束时间:%%s ;方法执行耗时:%%d%%n" , method.getName()), sdf.format(endTime), endTime - startTime); return result; } }); return proxyInstance; } /** * 使用CGLib创建动态代理对象 * 第三方提供的的创建代理对象的方式CGLib * 被代理对象不能用final修饰 * 使用的是Enhancer类创建代理对象 */ public static Object getObjectByCGLib(final Object obj){ /** * 使用CGLib的Enhancer创建代理对象 * 参数一:对象的字节码文件 * 参数二:方法的拦截器 */ Object proxyObj = Enhancer.create(obj.getClass(), new MethodInterceptor() { public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable { //方法执行前 long startTime = System.currentTimeMillis(); Object invokeObject = method.invoke(obj, objects);//执行方法的调用 //方法执行后 long endTime = System.currentTimeMillis(); SimpleDateFormat sdf = new SimpleDateFormat(); System.out.printf(String.format("%s方法执行结束时间:%%s ;方法执行耗时:%%d%%n" , method.getName()), sdf.format(endTime), endTime - startTime); return invokeObject; } }); return proxyObj; }
}
-
CGLib 底层原理 通过查看 Enhancer 类源码,最终也是生成动态代理类的字节码,动态代理类继承要被代理的类,然后实现其方法。
和 JDK Proxy 的实现代码比较类似,都是通过实现代理器的接口,再调用某一个方法完成动态代理的,唯一不同的是,CGLib 在初始化被代理类时,是通过 Enhancer 对象把代理对象设置为被代理类的子类来实现动态代理的。
CGLib 实现步骤 创建一个实现接口 MethodInterceptor 的代理类,重写 intercept 方法; 创建获取被代理类的方法 getInstance(Object target); 获取代理类,通过代理调用方法。
JDK Proxy 和 CGLib 的区别主要体现在以下方面:
JDK Proxy 是 Java 语言自带的功能,无需通过加载第三方类实现; Java 对 JDK Proxy 提供了稳定的支持,并且会持续的升级和更新,Java 8 版本中的 JDK Proxy 性能相比于之前版本提升了很多; JDK Proxy 是通过拦截器加反射的方式实现的; JDK Proxy 只能代理实现接口的类; JDK Proxy 实现和调用起来比较简单; CGLib 是第三方提供的工具,基于 ASM 实现的,性能比较高; CGLib 无需通过接口来实现,它是针对类实现代理,主要是对指定的类生成一个子类,它是通过实现子类的方式来完成调用的。