Java 那些事

JDK

jps(Java Virtual Machine Process Status Tool)

$JAVA_HOME/bin

# ls
appletviewer  jar        javadoc         javapackager  jconsole  jhat   jmc         jsadebugd  jvisualvm     pack200     rmiregistry  tnameserv  xjc
ControlPanel  jarsigner  javafxpackager  java-rmi.cgi  jcontrol  jinfo  jmc.ini     jstack     keytool       policytool  schemagen    unpack200
extcheck      java       javah           javaws        jdb       jjs    jps         jstat      native2ascii  rmic        serialver    wsgen
idlj          javac      javap           jcmd          jdeps     jmap   jrunscript  jstatd     orbd          rmid        servertool   wsimport

创建 BufferedReader

BufferedReader br = new BufferedReader(new InputStreamReader(System.in));

如果从文件读取

InputStream f = new FileInputStream("C:/java/hello");
BufferedReader br = new BufferedReader(new InputStreamReader(f));

可参考: https://www.cnblogs.com/52fhy/p/8232825.html

读取指定字节数量字符
public static void main(String[] args) throws IOException{
	InputStream input = new FileInputStream("src/test.txt");
	byte[] b = new byte[1024];
	int len = 0;
	StringBuffer sb = new StringBuffer("");

	while ((len = input.read(b)) > 0) {
		System.out.println(len);
		sb.append(new String(b, 0, len));
	}
	
	input.close();
	System.out.println(sb.toString());
}

字符串变成 BufferedReader
public static void main(String[] args) throws IOException{
		 String s="1\r\n2\r\n3\r\n \r\nabd\r\n";
		 BufferedReader br = new BufferedReader(new InputStreamReader(new ByteArrayInputStream(s.getBytes(Charset.forName("utf8"))), Charset.forName("utf8")));  
		 String line;  
		 StringBuffer strbuf=new StringBuffer();
		 while ( (line = br.readLine()) != null ) {  
		     if(!line.trim().equals("")){
		    	 line="<br>"+line;//每行可以做加工
		    	 strbuf.append(line+"\r\n");
		     }       		    	 
		 } 
	     System.out.println(strbuf.toString());
	}
String str = "Hello\nWorld\nJava";
BufferedReader reader = new BufferedReader(new StringReader(str));
String line;
while ((line = reader.readLine()) != null) {
    System.out.println(line);
}

String vs Bytes

String 和 Bytes 互转
  // string to byte[]
  byte[] bytes = "hello".getBytes(StandardCharsets.UTF_8);

  // byte[] to string
  String s = new String(bytes, StandardCharsets.UTF_8);

Synchronized 关键字的作用

  • synchronized是Java提供的一个并发控制的关键字,主要有两种用法,分别是同步方法块和同步代码块;
  • synchronized关键字可以给类或者对象进行加锁操作,保证共享资源在同一时间只会被一个线程访问到,这里要注意,对象锁针对的目标是实例化的对象,可以通过创建多个实例而实现多把锁,而类锁针对的目标是类文件,即Class本身,只存在唯一一个类锁;
  • synchronized保证原子性,在同步代码块或者同步代码方法中,即在锁未释放前,共享资源无法被其他线程访问到,通过这种方式实现了synchronized代码的原子性;
  • synchronized保证可见性,Java内存模型中规定了所有的变量都存储在主存,而每个线程又拥有自己的工作内存,线程对变量的所有操作都是基于工作内存的,而不能直接操作主存,synchronized保证,在解锁之前,必须把工作内存中的内存同步回主存中,通过这种方式保证了可见性;
  • synchronized保证有序性,这里的有序性并不是指synchronized杜绝了指令重排的情况,而是说目标代码在单线程的情况下不管如何指令重排,结果都是一致的,而synchronized实现了加锁条件下其他线程的不可访问,即实现了单线程的环境,在这种情况下synchronized代码是有序的。
一个源文件只能有一个public类

Java语言规范(Java Language Specification)规定,一个源文件只能有一个public类,并且这个类的名称必须与文件名相同。这意味着,如果你有一个public类,那么这个类的名称必须与文件名相同,并且这个类必须是这个源文件的顶层类(即不能是嵌套类)。

这种限制的原因在于,Java编译器需要把源文件编译成一个对应的.class文件,而每个.class文件只能包含一个public类。因此,如果一个源文件中有多个public类,那么编译器就无法生成对应的.class文件。

ProcessBuilder

/**
     * Returns the exit value for the process.
     *
     * @return the exit value of the process represented by this
     *         {@code Process} object.  By convention, the value
     *         {@code 0} indicates normal termination.
     * @throws IllegalThreadStateException if the process represented
     *         by this {@code Process} object has not yet terminated
     */
    public abstract int exitValue();

exitValue为非阻塞的,如果Process没有执行完毕,调用会抛出异常。
返回值,0代表正常退出,非0代表异常中止。

wait() 是阻塞method

wait() 使得子进程正在成为 caller 进程的子进程

    /**
     * Causes the current thread to wait, if necessary, until the
     * process represented by this {@code Process} object has
     * terminated.  This method returns immediately if the process
     * has already terminated.  If the process has not yet
     * terminated, the calling thread will be blocked until the
     * process exits.
     *
     * @return the exit value of the process represented by this
     *         {@code Process} object.  By convention, the value
     *         {@code 0} indicates normal termination.
     * @throws InterruptedException if the current thread is
     *         {@linkplain Thread#interrupt() interrupted} by another
     *         thread while it is waiting, then the wait is ended and
     *         an {@link InterruptedException} is thrown.
     */
    public abstract int waitFor() throws InterruptedException;
waitFor(long timeout, TimeUnit unit) 和 start()

这两个method 是异步method, 子进程成为了操作系统1号进程的子进程

测试小程序
sparkAppProcess.waitFor();
LOG.error("sparkAppProcess.isAlive():" + sparkAppProcess.isAlive());
LOG.error("sparkAppProcess:" + sparkAppProcess);

如果中途使用 kill -9 17168,
其输出结果为:

sparkAppProcess.isAlive():false
sparkAppProcess:Process[pid=17168, exitValue=137]
附:退出码说明.

Exit Code Number	Meaning	Example	Comments
1	Catchall for general errors	let “var1 = 1/0”	Miscellaneous errors, such as “divide by zero” and other impermissible operations
2	Misuse of shell builtins (according to Bash documentation)	empty_function() {}	Missing keyword or command, or permission problem (and diff return code on a failed binary file comparison).
126	Command invoked cannot execute	/dev/null	Permission problem or command is not an executable
127	“command not found”	illegal_command	Possible problem with $PATH or a typo
128	Invalid argument to exit	exit 3.14159	exit takes only integer args in the range 0 - 255 (see first footnote)
128+n	Fatal error signal “n”	kill -9 $PPID of script	$? returns 137 (128 + 9)
130	Script terminated by Control-C	Ctl-C	Control-C is fatal error signal 2, (130 = 128 + 2, see above)
255*	Exit status out of range	exit -1	exit takes only integer args in the range 0 - 25

退出代码137(128 + 9)表示您的进程被(信号9)SIGKILL终止。
如果没有手动停止脚本并仍然得到此错误代码,且脚本将被操作系统终止。在大多数情况下,这是由于过度使用内存造成的。

sparkAppProcess.destroyForcibly();
LOG.error("sparkAppProcess:" + sparkAppProcess);
if (sparkAppProcess.waitFor(TERMINATE_TIMEOUT_MS, TimeUnit.MILLISECONDS)) {
          LOG.error("At the end, sparkAppProcess:" + sparkAppProcess);
}
11:59:37.259 [main] ERROR  - sparkAppProcess:Process[pid=22508, exitValue="not exited"]
11:59:37.265 [main] ERROR  - At the end, sparkAppProcess:Process[pid=22508, exitValue=137]

由此可见 destroyForcibly 状态码也是137,其效果等同于 kill -9,
另外 使用destroyForcibly,需要等待一定的时长,才能拿到返回码,测试程序使用的等待时间是10s,但是实际日志,在不带1 s 的时间内返回。

加载 resource 目录下的文件
static {
        try {
            System.out.println("Debug DB path: " + xxxSparkUDF.class.getResource("/xx.ipdb"));

            BufferedInputStream streamIPV4 = new BufferedInputStream(xxxSparkUDF.class.getResourceAsStream("/xx.ipdb"));
            ipv4Parser = new IPV4Parser(streamIPV4);
            BufferedInputStream streamIPV6 = new BufferedInputStream(xxxSparkUDF.class.getResourceAsStream("/xx.awdb"));
            ipv6Parser = new xxParser(streamIPV6);
        } catch (Exception e) {
            e.printStackTrace();
            System.out.println(e.getMessage());
        }
    }

log4j

参考链接:
https://www.cnblogs.com/0201zcr/p/5725508.html
log4j.category 参考链接:
https://www.mobilefish.com/developer/log4j/log4j_quickguide_categories.html#google_vignette

指定模块输出日志
private static final Logger register = Logger.getLogger("register");

这里的"register",getLogger里的class 名,本质上是一样的,假如 getLogger传入的参数是本类类名。只用代码的 logger 表示 和 配置文件里能够匹配的上就可以。

##register模块输出 
#### 用来控制过来日志信息,如:下面至少是INFO的信息才会输出到register文件中
log4j.logger.register=INFO,register    
log4j.appender.register=org.apache.log4j.DailyRollingFileAppender   
log4j.appender.register.File=${logdir}/register.log
log4j.appender.register.DatePattern='_'yyyy-MM-dd'.log'
log4j.appender.register.layout=org.apache.log4j.PatternLayout  
log4j.appender.register.layout.ConversionPattern=%d %p [%c %L %l] - %m%n
log4j.additivity.register=true

Logger系统中有个根logger,是所有logger的祖先,它总是存在的,并且不可以通过名字获取,可以通过Logger.getRootLogger()来获取。

示例

https://github.com/apache/hadoop/blob/rel/release-3.2.2/hadoop-common-project/hadoop-common/src/main/conf/log4j.properties

hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/server/namenode/FSNamesystem.java

  /**
   * Logger for audit events, noting successful FSNamesystem operations. Emits
   * to FSNamesystem.audit at INFO. Each event causes a set of tab-separated
   * <code>key=value</code> pairs to be written for the following properties:
   * <code>
   * ugi=&lt;ugi in RPC&gt;
   * ip=&lt;remote IP&gt;
   * cmd=&lt;command&gt;
   * src=&lt;src path&gt;
   * dst=&lt;dst path (optional)&gt;
   * perm=&lt;permissions (optional)&gt;
   * </code>
   */
  public static final Log auditLog = LogFactory.getLog(
      FSNamesystem.class.getName() + ".audit");

在这里插入图片描述

spark 示例
spark.log.dir=/xxx/logs/
spark.audit.log.file=spark-audit.log
log4j.logger.xxclass=INFO, DFAAUDIT
log4j.additivity.xxclass=false
log4j.appender.DFAAUDIT=org.apache.log4j.DailyRollingFileAppender
log4j.appender.DFAAUDIT.DatePattern='.'yyyy-MM-dd-HH'.log'
log4j.appender.DFAAUDIT.File=${spark.log.dir}/${spark.audit.log.file}
log4j.appender.DFAAUDIT.layout=org.apache.log4j.PatternLayout
log4j.appender.DFAAUDIT.layout.ConversionPattern=%d{ISO8601} %p %c{2}: %m%n

这里和上述的 “register” 是同一个原理

spark 精确到 package 进行控制

修改这两项配置,可以实现该package 下 所有日志都能收集到

log4j.logger.org.apache.spark=INFO, DFAAUDIT
log4j.additivity.org.apache.spark=false
2023-12-13 12:06:00,577 INFO storage.BlockManager: Using org.apache.spark.storage.RandomBlockReplicationPolicy for block replication policy
2023-12-13 12:06:00,580 INFO storage.BlockManagerMaster: Registering BlockManager BlockManagerId(driver, 10.73.59.85, 55852, None)
2023-12-13 12:06:00,582 INFO storage.BlockManagerMasterEndpoint: Registering block manager 10.73.59.85:55852 with 366.3 MiB RAM, BlockManagerId(driver, 10.73.59.85, 55852, None)
2023-12-13 12:06:00,584 INFO storage.BlockManagerMaster: Registered BlockManager BlockManagerId(driver, 10.73.59.85, 55852, None)

log4j2

logger.audit.name = org.apache.spark.sql.execution.xxxListener
logger.audit.additivity = false
logger.audit.level = info
logger.audit.appenderRef.file.ref = auditFile

appender.auditFile.type = File
appender.auditFile.name = auditFile
appender.auditFile.fileName = /xxx/spark-audit.log
appender.auditFile.layout.type = PatternLayout
appender.auditFile.layout.pattern = %d{HH:mm:ss.SSS} %t %p %c{1}: %m%n%ex

logger.audit.additivity = false 表示 rootlogger 可以不用继承,也就是可以不用重复输出到文件

可更改的选项:

appender.auditFile.layout.pattern = %d{yyyy-MM-dd HH:mm:ss.SSS} %-5level %msg%n

######. RollingFile 示例

appender.xxx.name = xxx
appender.xxx.fileName = xx/spark-xxx.log
appender.xxx.layout.type = PatternLayout
appender.xxx.layout.pattern = %d{yyyy-MM-dd HH:mm:ss.SSS} %-5level %msg%n

appender.xxx.type = RollingFile
appender.xxx.filePattern = target/rolling2/test1-%d{MM-dd-yy-HH-mm-ss}-%i.log.gz
appender.xxx.policies.type = Policies
appender.xxx.policies.time.type = TimeBasedTriggeringPolicy
appender.xxx.policies.time.interval = 2
appender.xxx.policies.time.modulate = true
appender.xxx.policies.size.type = SizeBasedTriggeringPolicy
appender.xxx.policies.size.size=100MB
appender.xxx.strategy.type = DefaultRolloverStrategy
appender.xxx.strategy.max = 5
target/rolling2/test1-12-05-23-19-15-57-1.log.gz
target/rolling2/test1-12-08-23-18-42-01-1.log.gz
target/rolling2/test1-12-08-23-18-42-51-1.log.gz
spark 示例

变量名需要加上 “property.”,才能被引用和生效

property.spark.log.dir=/xxx/logs/

logger.audit.name = org.apache.spark.sql.execution.xxxListener
logger.audit.additivity = false
logger.audit.level = info
logger.audit.appenderRef.file.ref = auditFile

appender.auditFile.name = auditFile
appender.auditFile.fileName = ${spark.log.dir}/spark-audit.log
appender.auditFile.layout.type = PatternLayout
appender.auditFile.layout.pattern = %d{ISO8601} %p %c{2}: %m%n

appender.auditFile.type = RollingFile
appender.auditFile.filePattern = ${spark.log.dir}/spark-audit.%d{yyyy-MM-dd-HH}.log
appender.auditFile.policies.type = Policies
appender.auditFile.policies.time.type = TimeBasedTriggeringPolicy
appender.auditFile.policies.time.interval = 1
appender.auditFile.policies.time.modulate = true
提取环境变量

需要加上 “sys.”
其他环境变量参考 : https://logging.apache.org/log4j/2.x/manual/lookups.html#environment-lookup

appender.xxx.fileName = ${spark.log.dir}/spark-${sys:user.name}.log

配置项参考链接:
https://logging.apache.org/log4j/2.x/manual/configuration.html#Properties
https://howtodoinjava.com/log4j2/log4j2-properties-example/
https://www.cnblogs.com/yeyang/p/7944899.html

配置项解释

interval    integer 
此参数需要与filePattern结合使用,规定了触发rollover的频率,默认值为1。假设interval为4,若filePattern的date/time pattern的最小时间粒度为小时(如yyyy-MM-dd HH),则每4小时触发一次rollover;若filePattern的date/time pattern的最小时间粒度为分钟(如yyyy-MM-dd HH-mm),则每4分钟触发一次rollover。

DefaultRolloverStrategy
DefaultRolloverStrategy指定了当触发rollover时的默认策略。

DefaultRolloverStrategy是Log4j2提供的默认的rollover策略,即使在log4j2.xml中没有显式指明,也相当于为RollingFile配置下添加了如下语句。DefaultRolloverStrategy默认的max为7。

<DefaultRolloverStrategy max="7"/>

max参数指定了计数器的最大值。一旦计数器达到了最大值,过旧的文件将被删除。

注意:不要认为max参数是需要保留的日志文件的最大数目。

max参数是与filePattern中的计数器%i配合起作用的,其具体作用方式与filePattern的配置密切相关。

1.如果filePattern中仅含有date/time pattern,每次rollover时,将用当前的日期和时间替换文件中的日期格式对文件进行重命名。max参数将不起作用。

如,filePattern="logs/app-%d{yyyy-MM-dd}.log"

2.如果filePattern中仅含有整数计数器(即%i),每次rollover时,文件重命名时的计数器将每次加1(初始值为1),若达到max的值,将删除旧的文件。

如,filePattern=“logs/app-%i.log”

3.如果filePattern中既含有date/time pattern,又含有%i,每次rollover时,计数器将每次加1,若达到max的值,将删除旧的文件,直到data/time pattern不再符合,被替换为当前的日期和时间,计数器再从1开始。

假设fileName为logs/app.log,SizeBasedTriggeringPolicy的size为10KB,DefaultRolloverStrategy的max为3。

根据filePattern配置的不同分为以下几种情况:

时间格式转换

val DATE_FORMAT_STRING = "yyyy-MM-dd'T'HH:mm:ss.SSSz"

val format = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSz");
val date = new Date();
val str = format.format(date);


scala> val str = format.format(date);
str: String = 2023-12-21T21:24:17.014CST


val dataFormat = new SimpleDateFormat(DATE_FORMAT_STRING);


val submissionTime =  dataFormat.parse(str);

scala> submissionTime.getTime
res3: Long = 1703165057014

参考链接:
https://www.cnblogs.com/zjdxr-up/p/9673050.html

instanceof vs isInstance
  • instanceof 是一个操作符
if(a instanceof B){
}

表示:a 是不是 B 这种类型

  • isInstance是Class类的一个方法
public boolean isInstance(Object obj)
if(B.Class.isInstance(a)){

}

表示:
a 是否能强转为 B 类型

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值