java中位运算技巧

位运算符主要针对二进制,它包括了:“与”、“非”、“或”、“异或”。 运算符 含义 描述 【&】 按位与 如果两个相应的二进制位都为1,则该位的结果值为1,否则为0 【|】按位或 两个相应的二进制位中只要有一个为1,该位的结果值为1 【^】 按位异或 若参加运算的两个二进制位值相同则为0,否则为1 【~】 取反 ~是一元运算符,用来对一个二进制数按位取反,即将0变1,将1变0 【<<】 左移 用来将一个数的各二进制位全部左移N位,右补0 【>>】右移 将一个数的各二进制位右移N位,移到右端的低位被舍弃,对于无符号数,高位补0 1、“按位与”运算符(&) 按位与是指:参加运算的两个数据,按二进制位进行“与”运算。如果两个相应的二进制位都为1,则该位的结果值为1;否则为0。内存储存数据的基本单位是字节(Byte),一个字节由8个位(bit)所组成。位是用以描述电脑数据量的最小单位。二进制系统中,每个0或1就是一个位。将11(2)补足成一个字节,则是00000011(2),5的二进制编码是101(2),将其补足成一个字节,则是00000101(2) 按位与运算: 00000011(2) &00000101(2) 00000001(2) 由此可知3&5=1 按位与的用途: (1)清零 若想对一个存储单元清零,即使其全部二进制位为0,只要找一个二进制数,其中各个位符合一下条件: 原来的数中为1的位,新数中相应位为0。然后使二者进行&运算,即可达到清零目的。 例:原数为43,即00101011(2),另找一个数,设它为148,即10010100(2),将两者按位与运算: 00101011(2) &10010100(2) 00000000(2) (2)取一个数中某些指定位 若有一个整数a(2byte),想要取其中的低字节,只需要将a与8个1按位与即可。 a 00101100 10101100 b 00000000 11111111 c 00000000 10101100 (3)保留指定位: 与一个数进行“按位与”运算,此数在该位取1. 例如:有一数84,即01010100(2),想把其中从左边算起的第3,4,5,7,8位保留下来,运算如下: 01010100(2) &00111011(2) 00010000(2) 即:a=84,b=59 c=a&b=16 2、“按位或”运算符(|) 两个相应的二进制位中只要有一个为1,该位的结果值为1。借用逻辑学中或运算的话来说就是,一真为真 。 例如:60(8)|17(8),将八进制60与八进制17进行按位或运算。 00110000 |00001111 00111111 应用:按位或运算常用来对一个数据的某些位定值为1。例如:如果想使一个数a的低4位改为1,则只需要将a与17(8)进行按位或运算即可。 3、异或 例如:a=3,即11(2);b=4,即100(2)。 想将a和b的值互换,可以用以下赋值语句实现: a=a∧b; b=b∧a; a=a∧b; a=011(2) (∧)b=100(2) a=111(2)(a∧b的结果,a已变成7) (∧)b=100(2) b=011(2)(b∧a的结果,b已变成3) (∧)a=111(2)

ClassLoader类加载分析(二)

一、JVM 提供的 Classloader 1.1 BootstrapClassloader 引导类加载器,又称启动类加载器,是最顶层的类加载器,主要用来加载Java核心类,如rt.jar、resources.jar、charsets.jar等,Sun的JVM中,执行java的命令中使用-Xbootclasspath选项或使用- D选项指定sun.boot.class.path系统属性值可以指定附加的类,它不是 java.lang.ClassLoader的子类,而是由JVM自身实现的该类c 语言实现,Java程序访问不到该加载器。通过下面代码可以查看该加载器加载了哪些jar包 public class MainClass { public static void main(String[] args) throws ClassNotFoundException { URL[] urls = sun.misc.Launcher.getBootstrapClassPath().getURLs(); Arrays.stream(urls).map(URL::toExternalForm).forEach(System.out::println); } } 执行结果: file:/C:/java/jdk1.8.0_74/jre/lib/resources.jar file:/C:/java/jdk1.8.0_74/jre/lib/rt.jar file:/C:/java/jdk1.8.0_74/jre/lib/sunrsasign.jar file:/C:/java/jdk1.8.0_74/jre/lib/jsse.jar file:/C:/java/jdk1.8.0_74/jre/lib/jce.jar file:/C:/java/jdk1.8.0_74/jre/lib/charsets.jar file:/C:/java/jdk1.8.0_74/jre/lib/jfr.jar file:/C:/java/jdk1.8.0_74/jre/classes, 写到这里大家应该都知道,我们并没有在classpath里面指定这些类的路径,为啥还是能被加载到jvm并使用起来了吧,因为这些是bootstarp来加载的。 1.2 ExtClassloader 扩展类加载器,主要负责加载Java的扩展类库,默认加载JAVA_HOME/jre/lib/ext/目下的所有jar包或者由java.ext.dirs系统属性指定的jar包。放入这个目录下的jar包对所有AppClassloader都是可见的(后面会知道ExtClassloader是AppClassloader的父加载器)。那么ext都是在那些地方加载类内: System.out.println(System.getProperty("java.ext.dirs")); C:\java\jdk1.8.0_74\jre\lib\ext;C:\WINDOWS\Sun\Java\lib\ext 1.3 AppClassloader 系统类加载器,又称应用加载器,本文说的SystemClassloader和APPClassloader是一个东西,它负责在JVM启动时,加载来自在命令java中的-classpath或者java.class.path系统属性或者 CLASSPATH操作系统属性所指定的JAR类包和类路径。调用ClassLoader.getSystemClassLoader()可以获取该类加载器。如果没有特别指定,则用户自定义的任何类加载器都将该类加载器作为它的父加载器,这点通过ClassLoader的无参构造函数可以知道如下: protected ClassLoader() { this(checkCreateClassLoader(), getSystemClassLoader()); } 执行以下代码即可获得classpath加载路径: System.out.println(System.getProperty("java.class.path")); 1.4 Java中如何构造三种类加载器的结构 下面从源码来分析下JVM是如何构建内置classloader的,具体是rt.jar包里面sun.misc.Launcher类: public class Launcher { private static Launcher launcher = new Launcher(); private static String bootClassPath = System.

Java 内省(Introspector)

内省(Introspector) 是Java 语言对 JavaBean 类属性、事件的一种缺省处理方法。  JavaBean是一种特殊的类,主要用于传递数据信息,这种类中的方法主要用于访问私有的字段,且方法名符合某种命名规则。如果在两个模块之间传递信息,可以将信息封装进JavaBean中,这种对象称为“值对象”(Value Object),或“VO”。方法比较少。这些信息储存在类的私有变量中,通过set()、get()获得。  例如类Use : public class User { private String name; private String address; public String getName() { return name; } public void setName(String name) { this.name = name; } public String getAddress() { return address; } public void setAddress(String address) { this.address = address; } }  在类User中有属性 name, 那我们可以通过 getName,setName来得到其值或者设置新的值。通过 getName/setName来访问 name属性,这就是默认的规则。 Java JDK中提供了一套 API 用来访问某个属性的 getter/setter 方法,这就是内省。  JDK内省类库:

ClassLoader类加载分析(一)

一、什么是Classloader 一个Java程序要想运行起来,首先需要经过编译生成 .class文件,然后创建一个运行环境(jvm)来加载字节码文件到内存运行,而.class 文件是怎样被加载中jvm 中的就是Java Classloader所做的事情。 那么.class文件什么时候会被类加载器加载到jvm中运行那?比如执行new操作时候,当我们使用Class.forName(“包路径+类名”),Class.forName(“包路径+类名”,classloader),classloader.loadclass(“包路径+类名”);时候就触发了类加载器去类加载对应的路径去查找*.class,并创建Class对象。 类的加载过程 类从被加载到虚拟机内存中开始,到卸载出内存为止,它的整个生命周期包括: 1、装载 2-4、链接 -包括 【验证、准备、解析】 5、初始化 6、使用 7、卸载 其中 链接(Link)又分3个步骤,如图所示。类加载到卸载的生命周期流程图如下: 1) 装载:查找并加载类的二进制数据(查找和导入Class文件) 加载是类加载过程的第一个阶段,在加载阶段,虚拟机需要完成以下三件事情: 1、通过一个类的全限定名来获取其定义的二进制字节流(并没有指明要从一个Class文件中获取,可以从其他渠道,譬如:网络、动态生成、数据库等)。 2、将这个字节流所代表的静态存储结构转化为方法区的运行时数据结构。 3、在Java堆中生成一个代表这个类的java.lang.Class对象,作为对方法区中这些数据的访问入口。 相对于类加载的其他阶段而言,加载阶段(准确地说,是加载阶段获取类的二进制字节流的动作)是可控性最强的阶段,因为开发人员既可以使用系统提供的类加载器来完成加载,也可以自定义自己的类加载器来完成加载。 加载阶段完成后,虚拟机外部的 二进制字节流就按照虚拟机所需的格式存储在方法区之中,而且在Java堆中也创建一个java.lang.Class类的对象,这样便可以通过该对象访问方法区中的这些数据。 2) 链接(分3个步骤) 1、验证:确保被加载的类的正确性 验证是连接阶段的第一步,这一阶段的目的是为了确保Class文件的字节流中包含的信息符合当前虚拟机的要求,并且不会危害虚拟机自身的安全。验证阶段大致会完成4个阶段的检验动作: 文件格式验证:验证字节流是否符合Class文件格式的规范;例如:是否以0xCAFEBABE开头、主次版本号是否在当前虚拟机的处理范围之内、常量池中的常量是否有不被支持的类型。 元数据验证:对字节码描述的信息进行语义分析(注意:对比javac编译阶段的语义分析),以保证其描述的信息符合Java语言规范的要求;例如:这个类是否有父类,除了java.lang.Object之外。 字节码验证:通过数据流和控制流分析,确定程序语义是合法的、符合逻辑的。 符号引用验证:确保解析动作能正确执行。 验证阶段是非常重要的,但不是必须的,它对程序运行期没有影响,如果所引用的类经过反复验证,那么可以考虑采用-Xverifynone参数来关闭大部分的类验证措施,以缩短虚拟机类加载的时间。 2、准备:为类的静态变量分配内存,并将其初始化为默认值 准备阶段是正式为类变量分配内存并设置类变量初始值的阶段,这些内存都将在方法区中分配。对于该阶段有以下几点需要注意: 1、这时候进行内存分配的仅包括类变量(static),而不包括实例变量,实例变量会在对象实例化时随着对象一块分配在Java堆中。 2、这里所设置的初始值通常情况下是数据类型默认的零值(如0、0L、null、false等),而不是被在Java代码中被显式地赋予的值。 l例如在准备阶段,为类变量(static修饰)在方法区中分配内存并设置初始值。 private static int var = 50; 准备阶段完成后,var 值为0,而不是50。在初始化阶段,才会把50赋值给val,但是有个特殊情况: private static final int var= 50; 在编译阶段会为var生成ConstantValue属性,在准备阶段虚拟机会根据ConstantValue属性将var赋值为50。 3、解析:把类中的符号引用转换为直接引用 解析阶段是将常量池中的符号引用替换为直接引用的过程,解析动作主要针对类或接口、字段、类方法、接口方法、方法类型、方法句柄和调用限定符7类符号引用进行.。符号引用和直接引用有什么不同? 1、符号引用 :使用一组符号来描述所引用的目标,可以是任何形式的字面常量,定义在Class文件格式中。 2、直接引用 :可以是直接指向目标的指针、相对偏移量或则能间接定位到目标的句柄。 **3) 初始化: 初始化阶段是执行类构造器方法的过程,方法由类变量的赋值动作和静态语句块按照在源文件出现的顺序合并而成,该合并操作由编译器完成。 public class MuitiThreadInit { private static int value = 100; static int a = 100; static int b = 100; static int c; static { c = a + b; System.

设计模式之builder模式

建造者模式也叫生成器模式,和抽象工厂模式相似,也是一种构建复杂对象的模式。 建造者模式中的角色分类:  抽象建造者Builder:接口类型,用于规范各个产品的组成部分;  具体建造者ConcreteBuilder:实现Builder中的所有方法,返回一个产品实例;  指导者Director:指挥建造者制造相应的产品  产品Product:用户最终看到的复杂对象。 假设我们有一个向客户发送新年祝福邮件的需求,而邮件内容可以是纯文档的,也可以是有动画的,也可以是有音频的,可以动态的添加个组件 uml下图所示: 如上图所示,邮箱有多个组件,包含收件人,发送人,内容,音乐等 通过具体的建造者添加不同的组件模版,最后通过指挥者去调用抽象建造者 来返回具体的email对象 代码如下 ==================product====================== public interface Module { String showInfo(String info); }` public abstract class CommonModule implements Module { protected String moduleName; protected String productionTime() { return Optional.ofNullable(moduleName).orElse("") + "--> createTime : " + LocalDate.now().toString(); } } public class Sender extends CommonModule{ public Sender() { super.moduleName = "发件人"; } @Override public String showInfo(String info) { return Optional.ofNullable(info).orElse("sender : nicky@qq.

设计模式之factory模式

定义:工厂模式通俗意义上讲就是一个多产品的流程化工厂,每个工厂生产同一系列相关性的组件 分类: 按工厂职能划分可以分为三类:  简单工厂模式(Simple Factory)  工厂方法模式(Factory Method)  抽象工厂方法(Abstract Factory) 接下来我们直接通过uml图和具体的代码实现以上三类 我们以汽车工厂生产汽车为例,本田工厂假设要生产CIVIC和CRV两种车型,那么我们该怎么去设计实现 1 简单工厂模式 工厂产品线流程如上图所示:工厂生产汽车通过指定汽车的编号,我们就可以走指定的产品线;调用者只需要选择具体车型而不需要内部车是怎么制造出来的 简单工厂模式主要包括三部分: 工厂角色:上图中的HONDAFactroy 生产车型用 抽象产品角色:上图中的HONDA和HONDACar 包含通用的属性和方法 具体产品角色:上图中的CIVIC和CRV,具体的车型包含各种参数 代码如下 /** * @author nicky_chin [shuilianpiying@163.com] * @since –created on 2017/12/27 at 11:11 */ public class CIVIC extends HONDACar implements HONDA { private String carName; private Double price; public CIVIC() { this.carName = "10代思域"; this.price = 158000.9; } @Override public String introduceCar() { return "CIVIC{" + "carName='" + carName + '\'' + ", price=" + price + "} " + super.

可重入锁

可重入锁,也叫做递归锁,指的是同一线程 外层函数获得锁之后 ,内层递归函数仍然有获取该锁的代码,但不受影响。比如ReentrantLock 和synchronized 都是 可重入锁 直接放代码 public class SubService { ReentrantLock lock = new ReentrantLock(); public void subService1() { lock.lock(); System.out.println("thread = " + Thread.currentThread().getId() + " --subService1"); subService2(); lock.unlock(); } public synchronized void subService2() { System.out.println("thread = " + Thread.currentThread().getId() + " --subService2"); } } public class Service extends SubService { public synchronized void service1(){ System.out.println("thread = " + Thread.currentThread().getId() + " --service1"); service2(); } public synchronized void service2() { System.

库级优化之SHOW GLOBAL STATUS

慢查询 //查询慢线程情况 show global status like '%slow%' +---------------------+-------+ | Variable_name | Value | +---------------------+-------+ | Slow_launch_threads | 1 | | Slow_queries | 100 | +---------------------+-------+ 如果Slow_launch_threads值较大,说明有些东西正在延迟连接的新线程 //查询慢查询日志是否开启 show variables like '%slow%' +---------------------+--------------------------------------+ | Variable_name | Value | +---------------------+--------------------------------------+ |log_slow_admin_statements| ON | |log_slow_slave_statements| OFF | | slow_launch_time | 2 | | slow_query_log | ON | | slow_query_log_file | /home/mysql/mysql/slow_query.log | +---------------------+--------------------------------------+ log_slow_admin_statements表示是否将慢管理语句例如ANALYZE TABLE和ALTER TABLE等记入慢查询日志 log_slow_slave_statements 表示是否管理分区慢查询记录,一般不会去选择分区,所以不考虑 配置中一定要开启慢查询,这对服务器性能损耗并不大,如上面的打印结果得知:超过2秒即为慢查询,一共有100条慢查询 。当然我们在后台也可以做sql拦截记录,定制化慢查询语句。通常,可以通过mybatis插件拦截耗时sql打印日志 连接数 //mysql的最大连接数 show global status like 'max_connections'; +-----------------+-------+ | Variable_name | Value | +-----------------+-------+ | max_connections | 500 | +-----------------+-------+ 常见的问题"MYSQL: ERROR 1040: Too many connections"的异常情况,造成这种情况的一种原因是用户访问量过高, MySQL服务器抗不住也有可能是最大连接数设置的过小,需要注意 //服务器响应的最大连接数 show global status like 'max_used_connections'; +----------------------+-------+ | Variable_name | Value | +----------------------+-------+ | Max_used_connections | 450 | +----------------------+-------+ 设置的最大连接数是500,而响应的连接数是498,max_used_connections / max_connections * 100% = 90% (理想值 ≈ 85%)。

后端开发需要了解的mysql优化方向

#优化思维导图 #参数优化注意事项 参数优化分为 动态参数配置 和 配置文件的配置,建议在启动mysql之前配置好优化参数,这样将会全局有效,如使用动态参数配置可能会不生效或出现问题,并且如果数据库重启那么之前的优化参数都会失效 SHOW VARIABLES LIKE 'sort%' 修改会话级变量 set SESSION sort_buffer_size=720000 退出重新连接后,此参数恢复原值 修改全局变量 set GLOBAL sort_buffer_size = 720000 #优化系列 库级优化之SHOW GLOBAL STATUS

多线程之线程通信摘要

首先我们要知道进程之间的通讯方式有哪些? 管道( pipe ) #消息队列( message queue ) #共享内存( shared memory ) :#套接字( socket ) 等等– 线程的通讯方式: 1 wait/notify 机制 wait()方法和notify()方法是Object类提供的方法,而在使用的条件就是当前线程必须有自己的监听器 否则就是抛出异常,我们可以使用jvm提供的内置锁 synchronized 关键字来配合使用;注意如果有多个 线程等待,当某一线程发起唤醒操作,会随机唤醒一个线程,而非所有线程,如果想唤醒所有线程,可以使用 notifyAll()方法 下面是 启动一个等待线程和一个通知线程的例子 public class MyThread1 extends Thread { private Object lock; public MyThread1(Object lock) { this.lock = lock; } @Override public void run() { try { synchronized (lock) { System.out.println("wait start time = " + System.currentTimeMillis()); lock.wait(); System.out.println("wait end time = " + System.