JAVA知识点概括
BIO
阻塞式IO(BIO):数据在写入OutputStream或者InputStream都有可能会阻塞。如ServiceSocket在调用accept时,有客服端链接时,才会返回。一般连接之后会提供单独的线程去处理请求,当客服端大量的时候,存在大量线程,耗费资源,加重cpu的负担
弊端:
- 当前需要大量的Http长连接(淘宝)
- 需要同步资源(新的io操作)
客服端链接
- 创建Socket对象
- 连接建立后,通过输出流向服务器发送请求信息
- 通过输入流获取服务器响应的信息
- 关闭资源
//创建客户端socket,指定ip和端口
Socket socket = new Socket("localhost",10086);
//获取输入流,向服务器发送信息
//字节流
OutputStream os = socket.getOutputStream();
//封装为打印流
PrintWriter pw = new PrintWriter(os);
pw.write("我是socket 客户端");
//刷新
pw.flush();
socket.shutdownOutput();
//获取输入流,读取服务器响应信息
InputStream is = socket.getInputStream();
BufferedReader br = new BufferedReader(new InputStreamReader(is));
String info = null;
while ((info=br.readLine())!=null){
System.out.println("客户端:服务端发来的信息"+info);
}
br.close();
is.close();
pw.close();
os.close();
socket.close();
服务器端连接
- 创建ServiceSocket对象,绑定监听端口
- 通过accept()方法监听客户端请求
- 连接建立后,通过输入流读取客服端发送的请求信息
- 通过输出流向客服端发送信息
- 关闭相关的资源
//创建socket服务器端,端口
ServerSocket serverSocket = new ServerSocket(10086);
//调用accept方法,开始监听
Socket socket = serverSocket.accept();
//获取输入流
InputStream is = socket.getInputStream();
InputStreamReader isr = new InputStreamReader(is);
BufferedReader br = new BufferedReader(isr);
String info = null;
while ((info =br.readLine())!=null){
System.out.println("我是服务端:客服端发来消息:"+info);
}
socket.shutdownOutput();
//后去输入流响应请求
OutputStream os = socket.getOutputStream();
PrintWriter pw = new PrintWriter(os);
pw.write("欢迎你");
pw.flush();
pw.close();
os.close();
br.close();
isr.close();
socket.close();
serverSocket.close();
url
统一资源定位符,这个介绍简单使用,具体的没研究
public static void testURL() throws IOException {
URL url = new URL("https://www.baidu.com/");
InputStream inputStream = url.openStream();
InputStreamReader reader = new InputStreamReader(inputStream, "utf-8");
BufferedReader bufferedReader = new BufferedReader(reader);
String line = bufferedReader.readLine();
while (line != null) {
System.out.println(line);
line = bufferedReader.readLine();
}
inputStream.close();
reader.close();
bufferedReader.close();
}
Java高级IO
- 基于字节操作的I/O接口
- 基于字符操作的I/O接口
- 基于磁盘操作的I/O接口
- 基于网络操作的I/O接口
NIO
- 专门的线程处理IO事件
- 事件驱动机制(不是同步的监视事件)
- 有线程间的通信方式(同步资源等操作)
通信模型:
- 双向的通道
- 通道管理器Selector注册了事件,之后处理线程轮询,查看是否有对应的事件,处理即可
客服端code:
public class NIOClient {
//通道管理器
private Selector selector;
/**
* 获取一个Socket通道,并对该通道做了一些初始化的操作
*
* @param ip
* @param port
* @throws IOException
*/
public void initClient(String ip, int port) throws IOException {
//获得一个Socket通道
SocketChannel channel = SocketChannel.open();
//设置通道为非阻塞
channel.configureBlocking(false);
//获取通道管理器
this.selector = Selector.open();
//客服端连接服务器,其实方法执行并没有实现连接,需要在listen()方法中调
//用channel.finishConnect() 才能完成连接
channel.connect(new InetSocketAddress(ip, port));
//将通道管理器和该通道绑定,并为该通道注册SelectionKey.OP_CONNECT事件
channel.register(selector, SelectionKey.OP_CONNECT);
}
/**
* 轮询的方式监听 Selector是否有需要处理的事件,有则进行处理
*/
public void listen() throws IOException {
//轮询访问selector
while (true) {
selector.select();
//获得selector中选中的项的迭代器
Iterator<SelectionKey> ite = this.selector.selectedKeys().iterator();
while (ite.hasNext()) {
SelectionKey key = ite.next();
//删除已选的key,防止重复处理
ite.remove();
//连接事件发生
if (key.isConnectable()){
SocketChannel channel = (SocketChannel) key.channel();
//如果正在连接,则完成连接
if (channel.isConnectionPending()){
channel.finishConnect();
}
//设置程非阻塞
channel.configureBlocking(false);
//在这里给服务端发送信息
channel.write(ByteBuffer.wrap(new String("向服务器发送了信息").getBytes("utf-8")));
//在和服务端连接成功之后,为了可以收到服务端信息,需要给通道设置权限
channel.register(this.selector,SelectionKey.OP_READ);
}else if (key.isReadable()){
read(key);
}
}
}
}
private void read(SelectionKey key) throws IOException {
//读取消息,得到发生的socket通道
SocketChannel channel = (SocketChannel) key.channel();
//创建缓冲区
ByteBuffer buffer = ByteBuffer.allocate(1024);
channel.read(buffer);
byte[] data = buffer.array();
String msg = new String(data).trim();
System.out.println("client: "+msg);
}
}
服务端code:
public class NIOService {
//通道管理器
private Selector selector;
public void initServer(int port) throws IOException {
//获得一个ServerSocket通道
ServerSocketChannel serverChannel = ServerSocketChannel.open();
//设置非阻塞
serverChannel.configureBlocking(false);
//绑定到该端口
serverChannel.socket().bind(new InetSocketAddress(port));
//获得通道管理器
this.selector = Selector.open();
//将该通道和该通道绑定,并为该通道注册SelectionKey.OP_ACCEPT事件
//注册事件后,当该事件到达时,selector.select()会返回,如果该事件没有
//达到selector.select()会一直阻塞
serverChannel.register(selector, SelectionKey.OP_ACCEPT);
}
public void listen() throws IOException {
System.out.println("服务端启动");
//轮询
while (true) {
//注册事件到达时会返回,否则该方法会一直阻塞
selector.select();
//获得selector中的项的迭代器,选中的项为注册的事件
Iterator<SelectionKey> ite = this.selector.selectedKeys().iterator();
while (ite.hasNext()) {
SelectionKey key = ite.next();
//删除已选的key,以防重复处理
ite.remove();
//客服端请求连接事件
if (key.isAcceptable()) {
ServerSocketChannel server = (ServerSocketChannel) key.channel();
//获得和客服端的连接的通道
SocketChannel channel = server.accept();
//设置非阻塞
channel.configureBlocking(false);
//在这里可以给客服端发送信息
channel.write(ByteBuffer.wrap(new String("服务器发送信息").getBytes("utf-8")));
//在和客服端连接成功之后,为了可以接受到客服端的信息,需要给通道设置读的权限
channel.register(this.selector, SelectionKey.OP_READ);
} else if (key.isReadable()) {
read(key);
}
}
}
}
private void read(SelectionKey key) throws IOException {
//读取消息,得到发生的socket通道
SocketChannel channel = (SocketChannel) key.channel();
//创建缓冲区
ByteBuffer buffer = ByteBuffer.allocate(10);
channel.read(buffer);
byte[] data = buffer.array();
String msg = new String(data).trim();
System.out.println("service: "+msg);
}
}
多线程
共同点:通过thrad创建线程,通过start()方法开启就绪状态(具体的启动是有操作系统来决定的),直接调用run方法,没有线程效果,就是调用普通的方法。
//通过集成的方式
ThreadByExtends threadByExtends = new ThreadByExtends();
threadByExtends.start();
//通过实现的方式
ThreadImpByRunnable threadImpByRunnable = new ThreadImpByRunnable();
Thread thread = new Thread(threadImpByRunnable);
thread.start();
- 通过继承的方式 Thread
- 只能是单继承的方式的局限
- 创建一个线程,必须新建一个对象
- 通过实现的方式 Runnable
- 代码可以被多个进程共享
- 增强代码的健壮性
- 推荐使用
线程间通信
- 使用 synchronized关键字(常用)
- sleep/wait
- sleep阻塞线程,不会释放锁
- wait阻塞线程,会释放锁。
- wait/notify机制
- 在其他的线程,调用notiyf之后,通知锁已经释放了,wait才能去争取锁.
synchronized关键字
- 不同的线程操作同一个变量的时候,你在获取的时候是不一样的,创建线程的时候会拷贝变量(缓存的意思,线程会有独立的内存),修改变量没有立马通知更新,变量没有及时的更新。
- volatile关键字:用此关键字修饰的变量在获取的时候都是从主内存中获取的值,不允许线程将该变量拷贝到自己的内存空间.修改之后会通知所有线程,同步数据的改变,只能使用在变量上面
- synchronized :在获取是释放的时候都有一个监听器,会同步主内存中和所有线程中的值,性能叫volate有较大的消耗.
- lock: lock需要指定起始位置和终止位置;lock是java代码带执行的, synchronized 是jvm来实现的是较重的操作,一般运用lock需要在finally中 unlock 执行操作。synchronized是叫悲观锁的机制,是要释放对象,才能继续进行下一步,lock是不断的重试,直到可以进行下一步。
线程池
- 降低资源消耗
- 提高响应速度
- 提高线程的可管理性
/**
线程的基本大小
线程池最大线程数量,超过这个线程将不再创建线程
线程活动保持的时间,在适当时间线程可以复用
时间单位
当线程满了之后,会等待,之后放入对列中执行
创建线程的工厂
保护策略,当线程和队列满了之后的处理 AbortPolicy满了之后抛异常
*/
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler)
线程池的工作流程
- 先判断基本线程是否已满,没满创建工作线程,执行任务
- 线程池判断工作队列是否已满,未满该任务存储到任务队列中
- 整个线程池是否已满,执行保护策略
异常
- error 程序无法处理的错误
- Exceptioin 程序本身可以处理的异常
- 运行时异常 null异常等
- 非运行异常 编译不通过
异常处理机制
- try..catch 语句
- finally语句:任何情况下都必须执行的代码
- throws:声明可能抛出的异常,方法声明处
- throw:抛出异常,在各个地方
原理
- Java虚拟机用方法调用找,来跟踪每个线程中一系列的方法调用过程.
- 在执行的过程中抛出异常,jvm必须找打能捕获该异常的catch代码块
- 当jvm追溯到调用栈的底部的方法时,任然没有找到该处理该异常代码快
- 打印方法调用栈的信息
- 如果不是主线程终止该线程的信息。
异常处理
- 在try 中退出 System.exit(0),finally不被执行的情况
- 在catch 中抛出异常
- try中 有return语句,先执行finally语句,在执行return语句,如果有变量值在finally中改变,是不会成功的,值还是在try中的值。在finally代码块中不能通过给变量重新赋值的方式改变return语句的返回值。
try {
return 0;
} catch (Exception e) {
} finally {
}
return 0;
- 不要在 finally 中使用 return语句,坑会导致很多的问题,数据丢失,错乱。因为先执行finally语句,其中有return 语句,那么就不会执行 catch语句 。
Java中检查型异常和非检查型异常有什么区别?
Java检查型异常:对于申明抛出异常的方法,编译器将进行强制的处理
非检查型异常: error,runtimeexception
throw和throws两个关键字在java中有什么不同?
throws 出现在方法头部,表明该异常不能处理
throw: 方法内,抛出一个具体的异常
如果执行finally代码块之前方法返回的结果,或者jvm退出了,finally块中的代码会执行吗?
前者会执行,后者不会执行
Java中final,finalize,finally关键字区别
final:修饰符的关键字, 修饰类不能被继承,修饰变量必须初始化值,不能改变
finalize:方法名,jvm将在垃圾收集器的时候,将对象从内存清除出去.
注解
注解:Annotation(注解) 就是Java提供了一种元程序中的元素关联任何信息和着任何元数据(metadata)的途径和方法。
基本的规则:Annotation 不能影响程序代码的执行,无论增加,删除 Annotation,代码都始终如一的执行。
metadata(元数据):描述数据的数据,代码之间的关系。
元数据以标签的形式存在于java代码中
元数据描述的信息是类型安全的
元数据需要编译器之外的工具额外的处理用来生成其它的程序部件.
元数据可以存在Java源代码级别,也可以存在编译之后的class文件内部
系统注解
- @override 标记类型的注解 复写父类的方法
- @Deprecated 过时标识 @deprecated被java文档识别的,生成文档的!
- @SuppressWarning 关闭编译器的警告
@SuppressWarnings(value="unchecked")
元注解:
- @Target 修饰对象的范围
- @Retention 定义注解保留的长度
- @Documented
//修饰对象的范围
@Target(ElementType.FIELD)
//对象的生命周期
@Retention(RetentionPolicy.RUNTIME)
//程序的api
@Documented
//标注的类型是否可以被继承
@Inherited
public @interface Column {
}
Android support annotations
api 19出现的库
- @NonNull 直不能为null
- @Nullable 可以为空
- @StringRes 资源为String的id号
- @WorkerThread 在子线程当中
- @UiThread 在ui线程
- @CallSuper 重写父类的方法,父类的方法必须调用
ClassLoader
程序通过程序的需要,通过类的加载机制逐渐加载指定的class文件
开发人员来看:
- ClassLoader就是用来动态加载 class文件到内存中用的,没有继承classloader抽象类-
- BootStrap ClassLoader:启动类的加载器,加载jdk下面ari目录下的文件
- Extension ClassLoader:扩展加载器,加载jre ext目录的文件
- App ClassLoader :应用程序类加载器,负责加载用户路径的下的类,我么可以直接使用
jvm来看: - 启动类加载器,其实是jvm的一部分,内部是用 c++来写的
- 其他类的加载器,用java语言来实现的,独立虚拟级外,由启动类加载器加载到内存中,进行使用。
双亲委托模型
- 启动类加载器 ->扩展类加载器 ->应用程序加载器 ->自定义类加载器
- 并不是集成关系,而是组合的关系来实现的
- 首先有请求父类去加载该类,如父类没有相关的处理方法,再在自身或者子类上面去查找,尝试加载!
类加载的过程
- 加载
- 通过一个类的全限定名来获取其定义的二进制字节流
- 将这个字节流锁代表的静态存储结构转化为方法区的运行时数据结构
- 在java堆中生成已代表这个类的java.lang.Class对象,作为对方法区中这些数据的访问入口。
- 验证
- 确保Class文件中的字节流包含的信息符合当前虚拟机的要求
- 文件格式的验证,元数据的验证,字节码的验证和符号引用验证
- 准备
- 对基本数据类型进行赋值的处理
- 同时对static和final修饰的常量声明时显示赋值处理,被final修饰的在声明时也可以在类初始化之前赋值。
- 应用数据类型,没有显示为赋值为null
- 数据初始化 响应的元素复制默认的
- 解析:该过程是不确定的,在某些情况系,可能是在初始化之后.
- 类和接口的解析
- 字段解析
- 类方法的解析
- 接口方法解析
- 初始化
- 执行类构造器()方法的过程
- 使用
- 卸载
静态绑定:程序执行方法以前,是有编译器连接该方法
动态绑定:是在运行是有类去动态绑定。
Java堆和栈
静态分配(方法区):主要存放静态数据,全局static数据和常量,在整个程序期间都会存在。
栈分配 :方法执行时,局部变量在栈上创建区域,执行完毕后,会清楚,效率高但容量有限。
- 在方法体定义的(局部变量)一些基本类型的变量和对象的引用变量都是在方法的栈内存中分配的。
堆分配 :通常指的是在程序运行时直接new出来的内存
- 堆内存存放的new 创建的对象(包括该对象其中的成员变量)和数组,在堆中分配的内存,将有java垃圾回收器来自动管理。
- 运行时,垃圾回收,动态分配内存,存取速度较栈慢
java垃圾回收机制
java内存泄漏的原因
- 长生命周期的对象持有短生命周期对象的引用就很可能发生内存泄漏
反射
编译:将java代码转换为 .class文件,只纠正语法
- 编译时类型:编译时类型由声明该变量时使用的类型决定
- 在编译时,是调用声明类型的成员方法(多态的实现原理),也就是所谓的编译时类型的方法。
运行:java虚拟机执行 .class文件的过程
- 运行时类型是由实际赋给该变量的对象决定
- 在运行时,调用的是实际的类型的成员方法,也就是所谓的运行时类型的方法。
在调用引用实例的成员变量,无论是编译时,还是运行时,均是调用编译时类型的成员变量。
反射:在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能调用它的任意一个方法和属性。
生成一个.class文件,就会在 .class文件中生成一个 Class对象
获取class的三种方式
//方式一
Person person = new Person();
Class<? extends Person> clazz = person.getClass();
//获取包名和类名
System.out.println("包名:" + clazz.getPackage().getName() + " 完成类名: " + clazz.getName());
//方式二
Class<?> clazz1 = Class.forName("reflex.Person");
//创建对象
Person instance = (Person) clazz1.newInstance();
instance.setAge(10);
instance.setName("张三");
System.out.println(instance.toString());
//得到构造函数,创建对象
Constructor<?>[] constructors = clazz1.getConstructors();
Person person1 = (Person) constructors[0].newInstance();
//有参构造
Person person2 = (Person) constructors[1].newInstance("张三", 100);
//反射调用方法
Method getName = clazz1.getMethod("getName");
//有参函数
Method setName = clazz1.getMethod("setName", String.class);
setName.invoke(clazz1.newInstance(), "王武");
getName.invoke(clazz1.newInstance());
//方式三
Class<Person> calzz2 = Person.class;
Android反射相关
通过原始的java反射机制的方式调用资源,在调用三方的R文件的时候,通过反射获取!
定义一个资源工具类:
public class ResourceHelper {
private static String mPackageName = null;
private static Class<?> mLayout = null;
private static Class<?> mDrawable = null;
private static Class<?> mId = null;
private static Class<?> mAttr = null;
private static ResourceHelper mResourceHelper = null;
public static ResourceHelper newInstance(Context context) {
if (mResourceHelper == null) {
mPackageName = (mPackageName == null) ? context.getPackageName() : mPackageName;
mResourceHelper = new ResourceHelper(mPackageName);
}
return mResourceHelper;
}
public ResourceHelper(String packageName) {
try {
mLayout = Class.forName(mPackageName + ".R$layout");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
try {
mDrawable = Class.forName(mPackageName + ".R$drawable");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
try {
mId = Class.forName(mPackageName + ".R$id");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
try {
mAttr = Class.forName(mPackageName + ".R$attr");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
private int getResourceId(Class<?> classtType, String resourceName) {
if (classtType == null) {
throw new IllegalArgumentException("resclass is not initialized");
}
try {
Field field = classtType.getField(resourceName);
return field.getInt(resourceName);
} catch (NoSuchFieldException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
return -1;
}
public int getDrawableId(String resourceName) {
return getResourceId(mDrawable, resourceName);
}
public int getLayoutId(String resourceName) {
return getResourceId(mLayout, resourceName);
}
public int getId(String resourceName) {
return getResourceId(mId, resourceName);
}
public int getAttrId(String resourcename) {
return getResourceId(mAttr, resourcename);
}
测试验证在activiyt中
setContentView(ResourceHelper.newInstance(getApplicationContext()).getLayoutId("activity_main"));
Activity的启动过程中Activity的对象的创建
在android源码中 ActivityThread中找到 performLaunchActivity 类
Activity activity = null;
try {
java.lang.ClassLoader cl = appContext.getClassLoader();
activity = mInstrumentation.newActivity(
cl, component.getClassName(), r.intent);
StrictMode.incrementExpectedActivityCount(activity.getClass());
r.intent.setExtrasClassLoader(cl);
r.intent.prepareToEnterProcess();
if (r.state != null) {
r.state.setClassLoader(cl);
}
} catch (Exception e) {
if (!mInstrumentation.onException(activity, e)) {
throw new RuntimeException(
"Unable to instantiate activity " + component
+ ": " + e.toString(), e);
}
}
//newActivity方法跟进来,是运用反射的方式加载的
public Activity newActivity(ClassLoader cl, String className,
Intent intent)
throws InstantiationException, IllegalAccessException,
ClassNotFoundException {
return (Activity)cl.loadClass(className).newInstance();
}
在xml布局写的标签,等都是通过反射注入到java代码中.