随笔记,备忘录!
1. Logger之Logger.getLogger(Object.class)
使用System.out.println()
来调试,开发项目部署到生产环境,会因为众多的控制台输出降低应用的性能,而Log4J则可以很好平衡开发和部署应用,使用这个Logger
就可以代替print()打印了。
要使用Log4J最简单的方式就是在每一个类中,声明一个Logger私有属性——容易导致系统开销增加(每个使用log4j的类都增加了对象属性)和重复声明Logger对象,那么可以使用重构的方式声明一个公用的类Log4jUtils
来处理Log4j的公共操作。
(PS:如果Logger
没有getLogger
方法,则需要使用LoggerFactory
)
private final static Logger logger=Logger.getLogger(A.class)
如果要实验的话,首先在项目中配置log4j.properties
文件(经测试好像放在resources目录和类目录都可以),然后再写一个测试类LoggerTest
#日志级别,输出目的地
log4j.rootLogger=debug,stdout
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.Target=System.out
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
#输出格式
log4j.appender.stdout.layout.ConversionPattern=%c %d{ABSOLUTE} %5p %c{1}:%L - %m%n
测试代码执行debug
package com.example.loggertest;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class LoggerTest {
private final static Logger logger = LoggerFactory.getLogger(LoggerTest.class);
public static void main(String[] args) {
logger.debug("Hello World!");
}
}
代码架构
查看执行结果
重构一个公用属性类
Log4jUtils
来操作Logger的公共操作
package com.example.loggertest;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import sun.reflect.Reflection;
public class Log4jUtils {
public static Logger logger=null;
public static Logger getLogger(){
if(null==logger){
//java8 废弃Reflection.getCallerClass()——java的映射机制,解决在Log4jUtils得到调用logger对象的类型的问题
logger = LoggerFactory.getLogger(Reflection.getCallerClass().getName());
logger.debug("调用者类名:"+Reflection.getCallerClass().getName());
}
return logger;
}
}
修改LoggerTest中的测试代码
public class LoggerTest {
// private final static Logger logger = LoggerFactory.getLogger(LoggerTest.class);
public static void main(String[] args) {
Log4jUtils.getLogger().debug("Hello World!"); //调用公用类中的方法
}
}
执行结果
出现错误:堆栈报错,点进getCallerClass
源码查看,可以发现其中该方法是重载的(而带int型参数的方法是过时的,但是可用)
根据网上教程:尝试不同的int参数有不同的效果
1. 0:返回sun.reflect.Reflection
2. 1:返回当前类的Class对象
3. 2:返回调用该方法的Class对象
2.关键字 native final static
1. native
:Java平台有个用户和本地C代码进行互操作的API,称为Java Native Interface
(Java本地接口)。一个Native Method就是一个Java调用非Java代码的接口(并不提供实现体,有些像定义一个Java Interface,因为其实现体是由非Java语言在外面实现的)。方法的实现由非ava语言实现,比如C或C++。
①标识符native可以与所有其它的java标识符连用,但是abstract除外。因为native暗示这些方法是有实现体的,只不过这些实现体是非java的,但是abstract却显然的指明这些方法无实现体。比如native static表明这个方法可以在不产生类的实例时直接调用,这非常方便,比如当你想用一个native method去调用一个C的类库时。
java public class IHaveNatives{ native public void Native1( int x ) ; native static public long Native2() ; native synchronized private float Native3( Object o ) ; native void Native4( int[] ary ) throws Exception ; }
上面的第三个方法用到了native synchronized,JVM在进入这个方法的实现体之前会执行同步锁机制(就像java的多线程);
②native method的存在并不会对其他类调用这些本地方法产生任何影响,实际上调用这些方法的其他类甚至不知道它所调用的是一个本地方法。JVM将控制调用本地方法的所有细节。需要注意当我们将一个本地方法声明为final的情况。用java实现的方法体在被编译时可能会因为内联而产生效率上的提升。但是一个native final方法是否也能获得这样的好处却是值得怀疑的,但是这只是一个代码优化方面的问题,对功能实现没有影响;
③使用native关键字说明这个方法是原生函数,也就是这个方法是用C/C++语言实现的,并且被编译成了DLL,由java去调用。这些函数的实现体在DLL中,JDK的源代码中并不包含,你应该是看不到的。对于不同的平台它们也是不同的。这也是java的底层机制,实际上java就是在不同的平台上调用不同的native方法实现对操作系统的访问的。Java不是完美的,Java的不足除了体现在运行速度上要比传统的C++慢许多之外,Java无法直接访问到操作系统底层(如系统硬件等),为此Java使用native方法来扩展Java程序的功能。
④可以将native方法比作Java程序同C程序的接口,其实现步骤:
a. 在Java中声明native()方法,然后编译;
b. 用 javah 产生一个.h文件;
c. 写一个 .cpp 文件实现native导出方法,其中需要包含第二步产生的 .h 文件(注意其中又包含了JDK带的 jni.h 文件);
d. 将第三步的 .cpp 文件编译成动态链接库文件;
e. 在Java中用 System.loadLibrary() 方法加载第四步产生的动态链接库文件,这个native()方法就可以在Java中被访问了。
2. final
:可以修饰:属性,方法,类,局部变量(方法中的变量)
①修饰属性(成员变量):
a. 必须要赋初始值,而且是只能初始化一次;
b. 修饰的属性的初始化可以在编译期,也可以在运行期,初始化后不能被改变(表示基本类型的成员变量创建后是一个常数);
c. 修饰的属性跟具体对象有关,在运行期初始化的final属性,不同对象可以有不同的值;
d. 被final修饰的成员变量赋值,有两种方式:(1)直接赋值 (2)全部在构造方法中赋初值;
e. 如果修饰的成员变量是一个引用类型,则是说这个引用的地址的值不能修改,但是这个引用所指向的对象里面的内容还是可以改变的。
②修饰函数方法:
a. 修饰的方法不能被重写,表示该方法在子类中不能被重写;
b. 一个类的private方法会隐式的被指定为final方法。
③修饰类:
a. 修饰类当用final去修饰一个类的时候,表示这个类不能被继承。
b. 注意:(1) 被final修饰的类,final类中的成员变量可以根据自己的实际需要设计为final; (2) final类中的成员方法都会被隐式的指定为final方法;(3) 一般来说工具类我们往往都会设计成为一个fianl类。在JDK中,被设计为final类的有String、System等。
3. static
:在Java中并不存在全局变量的概念,但是我们可以通过static来实现一个“伪全局”的概念,在Java中static表示“全局”或者“静态”的意思,用来修饰成员变量和成员方法,也可以修饰代码块和内部类(静态内部类或嵌套内部类,内部类就是一个类定义在一个类内部)
①修饰属性:
a. 被static修饰的成员变量和成员方法是独立于该类的,它不依赖于某个特定的实例变量,也就是说它被该类的所有实例共享。所有实例的引用都指向同一个地方,任何一个实例对其的修改都会导致其他实例的变化;
b. 修饰的属性的初始化在编译期(类加载的时候),初始化后能改变;
c. 直接通过类名来进行访问——ClassName.propertyName;
d. 使用静态变量的功能作用:对象之间共享数据、访问方便。
②修饰方法:
a. static修饰的方法我们称之为静态方法,我们通过类名对其进行直接调用;
b. 修饰的属性、方法、代码段跟该类的具体对象无关,不创建对象也能调用static修饰的属性、方法等,直接通过类名来进行访问——ClassName.methodName(……);
c. 它不依赖于任何实例,所以static方法必须实现,也就是说他不能是抽象方法abstract。
③修饰代码块:
a. 被static修饰的代码块,我们称之为静态代码块,静态代码块会随着类的加载一块执行,而且他可以随意放,可以存在于该类的任何地方。
④注意点:
a. static和“this、super”势不两立,static跟具体对象无关,而this、super正好跟具体对象有关,故不能以任何形式引用this、super;
b. 只能调用static变量和调用static方法;
c. static变量在定义时必须要进行初始化,且初始化时间要早于非静态变量;
4. static final
和final static
:
①修饰:
a. 没什么区别,一般static写在前面;
b. static修饰的属性强调它们只有一个,final修饰的属性表明是一个常数(创建后不能被修改)。static final修饰的属性表示一旦给值,就不可修改,并且可以通过类名访问;
c. static final也可以修饰方法,表示该方法不能重写,可以在不new对象的情况下调用。
3.线程类的start()和run()方法、sleep()方法
start()方法和run()方法的详细对比了解见文章https://blog.csdn.net/weixin_45861518/article/details/111523126
另外,
为什么要用sleep
主要是为了暂停当前线程,把cpu片段让出给其他线程,减缓当前线程的执行。
方法的定义:
public static native void sleep(long millis) throws InterruptedException;
通过定义可以看出sleep方法是本地方法,通过系统调用暂停当前线程,而不是java自己实现的。 线程睡眠到期自动苏醒,并返回到可运行状态(就绪),不是运行状态。
sleep(long millis) 重载的方法:
public static void sleep(long millis, int nanos)
throws InterruptedException {
if (millis < 0) {
throw new IllegalArgumentException("timeout value is negative");
}
if (nanos < 0 || nanos > 999999) {
throw new IllegalArgumentException(
"nanosecond timeout value out of range");
}
if (nanos >= 500000 || (nanos != 0 && millis == 0)) {
millis++;
}
sleep(millis);
}
4. 在linux系统中运行jar包项目
在linux系统中运行jar包主要有以下四种方式。
java -jar XXX.jar
这是最基本的jar包执行方式,但是当我们用ctrl+c中断或者关闭窗口时,程序也会中断执行。java -jar XXX.jar &
&代表在后台运行,使用ctrl+c不会中断程序的运行,但是关闭窗口会中断程序的运行。nohup java -jar XXX.jar &
使用这种方式运行的程序日志会输出到当前目录下的nohup.out文件,使用ctrl+c中断或者关闭窗口都不会中断程序的执行。nohup java -jar XXX.jar >temp.out &
>temp.out
的意思是将日志输出重定向到temp.out文件,使用ctrl+c中断或者关闭窗口都不会中断程序的执行。