- 博客(31)
- 收藏
- 关注
原创 Groovy基础
在使用动态语言编写程序时,变量的类型不需要在声明时明确指定,而是在运行时根据赋给变量的值来确定类型。Closure.DELEGATE_FIRST,优先级:delegate > owner,闭包首先在其delegate上查找属性和方法,如果找不到,则在owner上查找。Closure.OWNER_FIRST,优先级:owner > delegate,闭包首先在其owner上查找属性和方法,如果找不到,则在delegate上查找。总结:如果闭包定义在类中,则owner指向的是类的实例;,而不需要显式声明参数。
2025-02-08 19:21:27
1009
1
原创 手写一个路由框架实现页面跳转
相信大家现在已经知道框架是怎么做出来的吧,先完成需求,再慢慢优化我们这个框架用到了:APT、反射、PMS、类加载、javapoet等技术。
2024-11-25 19:01:48
913
原创 第一个设计模式——单例模式
1.反序列化破坏:将一个单例对象进行序列化后,再反序列化,而反序列化的对象和程序对象不是同一个对象。缺点:可能被反序列化和反射破坏、浪费资源,当你不需要单例实例,只想调用类中的静态方法时,它都会帮你执行静态代码块和静态变量,因为类加载。优点:是线程安全的,因为JVM在进行类加载的时候,会进行加锁,每个类只有一份class对象,然后类加载的时候就会执行静态代码块、静态变量。缺点:不能继承其它类,因为它内部已经继承了Enum类、它的线程安全是依靠类加载,但是类加载是耗性能的。指在类加载时就完成了初始化。
2024-07-28 19:14:13
1001
原创 三张表,搞懂HashMap的基础
怎么解决hash冲突?当由key计算出来的hash相同时,HashMap使用的是链表或红黑树来解决。3.当数组的元素数量已经到达当前容量的75%(默认阈值是0.75),则进行扩容。2.当存在链表节点长度>=8,但数组长度小于64时,则进行扩容。1.数组为空,首次扩容。每次扩容原来的2倍长的新数组。什么时候会进行扩容?
2024-07-28 11:02:11
313
原创 HashMap详细讲解
如果不为空则进行添加元素,先通过(n-1)&hash计算出在数组的索引值,然后判断该索引是否有数据,如果没有则直接创建一个Node存到该索引上,如果有则说明该索引上已有数据(称为hash冲突),那么就有两种情况:第一种情况是当该索引上的Node的key是否与我们传进来的key相同并且hash相同,如果相同,则覆盖value;如果不是,则走链表的添加流程,通过循环找到next为null的Node,当在循环的过程中,如果找到了和传入的key一样的key,hash值也一样,那么就覆盖value;
2024-07-28 10:52:52
1048
原创 java的八大包装类
复杂操作的场景,场景1:比如定义一个Integer类型的变量,用它作为一个方法的返回值,这个方法经过一系列的操作,最后得到一个Integer类型的数据,我们并不知道他最后返回一个什么东西,有可能是数字,也有可能是个null值,如果我们用int类型来接收null的话,就会出现空指针异常,这时候就得用包装类来接收。场景2:再比如我们想要得到int的最大值,这时基本数据类型是得不到的,但包装类可以,它内部有一个静态变量,该变量存的是int类型的最大值。1.由于包装类是个类,它有丰富的方法,可以做复杂的功能。
2024-07-27 16:48:52
1200
原创 还搞不清楚String、StringBuilder、StringBuffer?
代码解释:在堆中创建StringBuilder对象,然后builder指向该对象,"hello"存在StringBuilder 的成员变量value中,value是指向堆的一个byte[]对象,这个byte[]对象的值为"hello"。代码解释:在堆中创建StringBuffer对象,然后buffer指向该对象,"hello"存在StringBuffer的成员变量value中,value是指向堆的一个byte[]对象,这个byte[]对象的值为"hello"。如果大于0,则需要扩容,然后通过复制达到扩容。
2024-07-27 10:56:46
2288
原创 线程并发工具类——fork-join
分而治之是一个算法设计思想,它就是把大问题分割为多个相同的小问题,并且小问题之间无关联,如果小问题之间有关联就叫做动态规划。像我们的归并排序算法就利用这个思想二、Fork-Join就体现了分而治之原理:就是在必要情况下,将一个大任务,进行拆分(fork)成若干个小任务(拆到不可再拆时),再将一个个小任务运算的结果进行join汇总起来。
2024-07-23 14:53:25
611
原创 线程的协作——线程之间相互配合,完成某项工作
例如:一个线程修改了一个对象的值,而另一个线程感知到了变化,然后进行相应的操作,整个过程开始于一个线程,而最终执行又是另一个线程,前者是生产者,后者是消费者。要想做到上面的需求,我们常规思路是:让消费者线程不断地循环检查变量是否符合预期在while循环中设置不满足的条件,如果条件满足则退出while循环,从而完成消费者的工作。但是却存在如下问题:1)难以确保及时性2)难以降低开销,如果降低睡眠的时间,比如休眠1。
2024-07-22 21:48:40
427
原创 四大引用——强软弱虚
强引用是在程序代码之中普遍存在的,类似于“Object obj = new Object()”,obj变量引用Object这个对象,就叫做强引用。“虚引用”顾名思义,就是形同虚设,与其他几种引用都不同,虚引用并不会决定对象的生命周期。如果一个对象仅持有虚引用,那么它就和没有任何引用一样,在任何时候都可能被垃圾回收器回收。弱引用(WeakReference)也是用来描述非必需对象的,但是它的强度比软引用更弱一些,每次执行GC的时候,一旦发现了只具有弱引用的对象,不管当前内存空间足够与否,都会回收它的内存。
2024-07-22 20:37:49
580
原创 使用ThreadLocal——解决线程共享安全问题
ThreadLocal是一个类,为每个线程都提供了变量的 副本,使得每个线程在某一时间访问到的并非同一个对象,这样就隔离了多个线程对数据的数据共享。
2024-07-22 19:54:51
1127
原创 volatile,最轻量的同步机制
volatile关键字只保证了线程的可见性volatile关键字不能代替synchronized关键字volatile关键字不能保证线程安全volatile关键字适用于一个线程写,多个线程读的场景。
2024-07-22 15:10:16
512
原创 使用synchronized关键字(基础篇)——解决线程共享安全问题
我们先来看一段代码:代码解释:我们开启两个线程去完成count++任务,一个线程加一万次,那么count值为10000,两个线程的话结果应该是20000,但是结果事与愿违,可以得出在多线程下,共同访问一个资源会引发安全问题。什么是线程间的共享?在一个进程中会有多个线程,而这些线程共享进程的资源,就称作线程间的共享。
2024-07-22 10:44:37
460
原创 java线程之守护线程
守护线程也称“服务线程”,它是后台线程,在没有用户线程可服务时会自动离开。主线程、我们自己开启的线程(没有设置为守护线程)就是用户线程。
2024-07-21 20:37:36
468
原创 线程的状态
该状态的线程位于可运行线程池中,等待被线程调度选中,获取。超时等待(TIMED_WAITING)运行状态(RUNNABLE)终止(TERMINATED)阻塞状态(BLOCKED)等待状态(WAITING)
2024-07-21 19:50:50
170
原创 面试官:如何实现线程按顺序执行?
我们先看一段代码:根据我们常规的思路来想(没学线程),我们先启动t1线程,再启动t2线程,肯定是t1先执行,然后执行t2,对不对?但是我们运行多次,会出现t2先执行,t1再执行!这是为什么?是因为当线程调用start方法时,该线程就会等待被分配CPU,一旦获取到了CPU,就可以开始工作。CPU是操作系统进行分配的,分配给谁是不确定的,有可能t1先得到CPU,先执行;有可能t2先得到CPU,先执行;有可能t1和t2同时拿到。这都是说不准的。那用什么方法可以确定线程的执行顺序呢?
2024-07-21 17:14:02
325
原创 深入理解run和start方法
我们都知道Thread类是java里对线程概念的抽象,当我们通过new Thread(),其实只是new了一个Thread的实例,还没有和操作系统中的真正的线程挂起钩来。
2024-07-21 16:32:21
309
原创 如何停止线程工作呢?
定义一个变量,由目标线程去不断的检查变量的状态,当变量达到某个状态时停止线程。我们可以看出t1线程中使用变量来判断是否停止执行业务逻辑,使用Thread.sleep方法让t1线程睡一会,睡醒了,看看flag标志位是否被更改,如果被更改,说明需要停止工作。但是定义变量是有问题的,分为两种情况:1.当标志位已经被我们更改,线程还在睡,睡醒了才能判断标志位是否被更改,这会导致不能及时停止线程工作问题。2.
2024-07-21 14:46:31
2100
原创 Java创建线程的四种实现方式
我们首先看看Thread的构造方法有哪几种,从代码中可以看出我们只传了一个参数进去,说明task要么是Runnable类型,要么是String类型,肯定不是String类型。所以我们可以大胆猜测一下,FutureTask是不是实现了Runabble接口。在上面我们可以看出Runnable的run方法不能定义返回值,Callbale接口的call方法可以定义返回值,只是使用有点复杂。可以看出,FutureTask在run方法中调用了Callable接口的call方法!后续会更新,请耐心等待。
2024-07-19 21:16:09
265
原创 高并发编程的意义和注意事项
例如我们在实现电商系统的时候,用户一旦下单,我们就会去发短信通知用户,那用户关心你这个是怎么发送短信的吗?肯定不关心啊,所以我们就可以将向用户发送短信独立为单独的模块,并交给其它线程去执行,这样既增加了异步操作,提升了系统性能,又使程序模块化、简单化。是在面对大量并发请求时,为了保证系统的性能和处理能力,采用相应的编程技术和策略来处理并发任务。例如我们现在下载电视剧的时候,是不是不会一集一集的下,而是同时下载3,4集等等,为什么?多线程是实现高并发编程的一种常见技术和手段。可以使你的代码模块化。
2024-07-19 20:16:20
383
原创 并发和并行
比如单 CPU 核心下执行多线程并非是同时执行多个任务,如果你开两个线程执行,就是在你几乎不可能察觉到的速度不断去切换这两个任务,已达到"同时执行效果",其实并不是的,只是计算机的速度太快,我们无法察觉到而已。例如:吃饭的时候可以边吃饭边打电话,这两件事情可以同时执行。并发是指应用在同一时间间隔能够交替执行不同的任务。并行是指应用在同一时刻能够同时执行不同的任务。
2024-07-19 15:44:25
258
原创 CPU 时间片轮转机制(RR调度)
系统将所有的就绪进程按先来先服务的原则,排成一个队列,每次调度时,把CPU分配给队首进程,并令其执行一个时间片。当执行的时间片用完时,由一个计时器发出时钟中断请求,调度程序便据此信号来停止该进程的执行,并将它送往就绪队列的末尾;但如果顾客太多,或者每个人的饭量不一样(进程的任务大小不同),就需要合理安排每个人的服务时间(时间片的长度),以确保大家都满意。操作系统给每个进程分配一段可运行的时间,该时间就叫做时间片,如果在时间片结束时进程还在运行,则CPU将被剥夺并分配给另一个进程。
2024-07-18 20:54:11
666
原创 java线程必学的进程和线程的概念
比如:在java中,如果你想运行一个java程序,就必须有一个main函数(main函数中啥也不写或者写一点代码),main函数一旦执行,就必定会有一个主线程。CPU 调度的最小单位,是真正执行任务的,必须依赖于进程而存在,一个进程会有多个线程,线程无处不在。线程(Thread)
2024-07-18 19:10:39
222
原创 检查死锁的三种方法
堆栈信息和jstack显示的差不多,堆栈信息中间部分比jstack多出一些信息。该工具是jdk提供的工具,在bin包下。该工具也是jdk提供的,也在bin包下。打开工具,然后选择程序,会自动检测死锁。检测死锁后,可以看到存在死锁的线程。首先使用jps查看Java进程编号。然后使用jstack查看进程信息(),出现以下信息,表示出现了死锁。打开工具,选择要检测的程序。jstack 进程编号。
2024-07-18 18:15:01
1015
原创 Android-----获取系统状态栏和导航栏的高度
3、当我们将一个view的x、y坐标赋了event.x和event.y,希望让view跟着我们触摸点移动时,但会发现我们虽然将触摸点赋给了view,view和我们的触摸点会有一段距离,这段距离就是(状态栏/导航栏/状态栏+导航栏)原因:在onCreate()、onStart()、onResume()中,Activity的视图层级还没有完全建立,DecorView还没有渲染出来,所以会导致无法获取高度。------------------前提是有状态栏或者导航栏------------------
2023-10-18 20:23:53
2093
原创 Activity和Fragment的生命周期
首先点击button进入下一个activity,然后点击back回到上一个activity,最后再点击button进入下一个activity。到第二个Fragment的时候,再触发触摸事件,再进行到第二个Fragment。触摸屏幕,运行触摸事件,进入下一个Fragemt。点到第二个Fragment,将应用保留到后台。点击button跳转到下一个界面。进入应用,然后将app保留到后台。在点击back,回到主屏幕。点击back,回到上一步。点击back,回到主屏幕。点击back,回到上一步。
2023-07-17 08:31:47
156
1
原创 指针的相关知识
a.malloc()——首次分配eg:mallo(需要多大*sizeof(类型))a.当定义时,只为指针变量分配了8个自己的内存空间(用于存储地址)当对指针加或者减时,操作的是指针指向的元素占据的内存空间*数量。b.如果需要存数据,就必须为这个指针变量指向的区域分配内存空间。a.定义数组时,必须指定元素个数,数组的内容空间由系统分配。b.指针占8个字节的内存空间,和指向的类型没有关系。b.使用地址访问 *(a+0) *(a+1)c.free()——释放自己申请的内存空间。a.指针变量需要内存空间。
2022-11-26 22:44:08
85
空空如也
空空如也
TA创建的收藏夹 TA关注的收藏夹
TA关注的人