Code Monkey home page Code Monkey logo

lsyf.github.io's People

Contributors

lsyf avatar

Watchers

 avatar

lsyf.github.io's Issues

Java 日志简介1

1. 用处

  • 用来记录程序的运行状态
  • 可控输出粒度
  • 可控输出方式

2. 组件

1. Logger 日志输出粒度

  • Level: 定义级别
  • Threshold: 定义门槛,当Threshold>Level时,使用该级别(两者选最高级别)

2. Appender 日志输出方式

  • Additivity:自定义输出方式不会覆盖而是追加,可设置additivity为false来避免

3. Layout 日志输出格式

3. 结构

1. 组成结构

1562408147874

2. 执行流程

image

3. 类结构图

image

Maven pom.xml简介

1. Maven 坐标( Coordinate )

  • groupId:组织标识,一般为:公司网址的反写+项目名
  • artifactId:项目名称,一般为:项目名-模块名
  • version:版本号,一般为主版本.次版本.增量版本-限定版本号
  • packaging:打包的方式,如:pom, jar, maven-plugin, ejb, war, ...

2. dependencies

依赖调节原则: 1. 路径最近者优先; 2. 第一声明者优先
A–>B–>C。当前项目为A,A依赖于B,B依赖于C。知道B在A项目中的scope,那么怎么知道C在A中的scope呢?答案是:
当C是test或者provided时,C直接被丢弃,A不依赖C;
否则A依赖C,C的scope继承于B的scope。

  • groupId、artifactId和version :依赖坐标
  • type: 依赖的类型, 对应于项目坐标定义的packaging,默认jar
  • scope: 依赖的范围, 用来控制依赖与三种classpath(编译classpath测试classpath运行classpath)的关系
    • compile:默认范围,编译依赖项在项目的所有类路径中都可用。此外,这些依赖项将传播到依赖项目
    • provided:意味着打包的时候可以不用包进去,别的设施(Web Container)会提供。事实上该依赖理论上可以参与编译,测试,运行等周期。相当于compile,但是在打包阶段做了exclude的动作
    • runtime:表示被依赖项目无需参与项目的编译,不过后期的测试和运行周期需要其参与。例如oracle jdbc驱动架包。通常和optional搭配使用,optional为true。我可以用A实现,也可以用B实现
    • test:表示依赖项目仅仅参与测试相关的工作,包括测试代码的编译,执行。比较典型的如junit
    • system:从参与度来说,和provided相同,不过被依赖项不会从maven仓库抓,而是从本地文件系统拿,一定需要配合systemPath属性使用,提供指定的jar
    • import:此范围仅pom在该<dependencyManagement>部分中的类型依赖项上受支持。从其他项目导入托管依赖项,例如spring-cloud-dependencies
  • optional:依赖是否可选,为false时不包括在classpath下,如使用需直接声明该依赖
  • exclusions:排除传递性依赖

3. properties

加载顺序:

  • <build><filters> 中的配置
  • pom.xml 中的 <properties>
  • mvn -Dproperty=value 中定义的 property
    相同 key 的 property,以最后一个文件中的配置为最终配置。

4. build

  • defaultGoal:执行build任务时,如果没有指定目标,将使用的默认值,如:在命令行中执行mvn,则相当于执行mvn install;
  • directory:build目标文件的存放目录,默认在 ${basedir}/target目录
  • finalName:build目标文件的文件名,默认情况下为${artifactId}-${version}
  • filter:定义*.properties文件,包含一个properties列表,该列表会应用的支持filter的resources中。也就是说,定义在filter的文件中的"name=value"值对会在build时代替 ${name} 值应用到 resources 中。Maven的默认filter文件夹是 ${basedir}/src/main/filters/

Resources

  • resources:一个resource元素的列表,每一个都描述与项目关联的文件是什么和在哪里;
  • targetPath:指定build后的resource存放的文件夹。该路径默认是basedir。通常被打包在JAR中的resources的目标路径为META-INF;
  • filtering:true/false,表示为这个resource,filter是否激活。
  • directory:定义resource所在的文件夹,默认为 ${basedir}/src/main/resources
  • includes:指定作为resource的文件的匹配模式,用 * 作为通配符;
  • excludes:指定哪些文件被忽略,如果一个文件同时符合 includesexcludes,则 excludes生效;
  • testResources:定义和 resource 类似,但只在 test 时使用,默认的 test resource文件夹路径是 ${basedir}/src/test/resourcestest resource 不被部署。

plugins

  • extensions:是否加载该插件的扩展,默认false
  • inherited:该插件的 configuration 中的配置是否可以被(继承该POM的其他Maven项目)继承,默认true
  • configuration:该插件所需要的特殊配置,在父子项目之间可以覆盖或合并
  • dependencies:该插件所特有的依赖类库
  • executions:plugin 可以有多个目标,每一个目标都可以有一个分开的配置,甚至可以绑定一个 plugin 的目标到一个不同的阶段。executions 配置一个 plugin 的目标的 execution。一个 execution 有如下设置:
    • id,唯一标识
    • goals,要执行的插件的 goal(可以有多个),如 <goal>run</goal>
    • phase,目标执行的阶段,具体值看Maven的生命周期列表
    • inherited,该 execution 是否可被子项目继承
    • configuration,该 execution 的其他配置参数

5. 完整pom.xml

<project xmlns="http://maven.apache.org/POM/4.0.0"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
                      http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>
 
  <!-- 项目的全球唯一标识符,通常使用全限定的包名区分该项目和其他项目。并且构建时生成的路径也是由此生成, 如com.seyvoue.demo生成的相对路径为:/com/seyvoue/demo--> 
  <groupId>com.seyvoue.demo</groupId>
  <!-- 构件的标识符,它和 groupId 一起唯一标识一个构件。换句话说,你不能有两个不同的项目拥有同样的 artifactId 和 groupId;在某个特定的 groupId 下,artifactId也必须是唯一的。-->
  <artifactId>demo-maven</artifactId>
  <!-- 项目当前版本,格式为:主版本.次版本.增量版本-限定版本号-->
  <version>1.0.0-SNAPSHOT</version>
  <!-- 项目产生的构件类型,例如 jar、war、pom 等。插件可以创建他们自己的构件类型,所以前面列的不是全部构件类型-->
  <packaging>jar</packaging>
  <!-- 项目的名称,Maven产生的文档用-->     
  <name>project-demo</name>
  <!-- 项目主页的URL,Maven产生的文档用-->
  <url>http://demo.seyvoue.com</url>
  <!-- 项目的详细描述, Maven 产生的文档用。当这个元素能够用HTML格式描述时(例如,CDATA中的文本会被解析器忽略,就可以包含HTML标签), 不鼓励使用纯文本描述。如果你需要修改产生的web站点的索引页面,你应该修改你自己的索引页文件,而不是调整这里的文档。-->
  <description>A demo of maven project to study maven.</description>
  


  <parent>    
    <!--被继承的父项目的构件标识符-->    
    <artifactId/>    
    <!--被继承的父项目的全球唯一标识符-->    
    <groupId/>    
    <!--被继承的父项目的版本-->    
    <version/>    
    <!-- 父项目的pom.xml文件的相对路径。相对路径允许你选择一个不同的路径。默认值是../pom.xml。Maven首先在构建当前项目的地方寻找父项目的pom,其次在文件系统的这个位置(relativePath位置),然后在本地仓库,最后在远程仓库寻找父项目的pom。-->    
    <relativePath/>    
 </parent>


  <!-- 继承自该项目的所有子项目的默认依赖信息。这部分的依赖信息不会被立即解析,而是当子项目声明一个依赖(必须描述group ID和 artifact ID信息),如果group ID和artifact ID以外的一些信息没有描述,则通过group ID和artifact ID 匹配到这里的依赖,并使用这里的依赖信息。-->    
  <dependencyManagement>    
    <dependencies>    
      <!--参见dependencies/dependency元素-->    
      <dependency>    
      ...    
      </dependency>    
    </dependencies>    
 </dependencyManagement>



  <!--该元素描述了项目相关的所有依赖。 这些依赖组成了项目构建过程中的一个个环节。它们自动从项目定义的仓库中下载。要获取更多信息,请看项目依赖机制。-->     
  <dependencies>     
    <dependency>    
      <!--依赖的group ID-->    
      <groupId>org.apache.maven</groupId>     
      <!--依赖的artifact ID-->    
      <artifactId>maven-artifact</artifactId>     
      <!--依赖的版本号。 在Maven 2里, 也可以配置成版本号的范围。-->    
      <version>3.8.1</version>     
      <!-- 依赖类型,默认类型是jar。它通常表示依赖的文件的扩展名,但也有例外。一个类型可以被映射成另外一个扩展名或分类器。类型经常和使用的打包方式对应,   这也有例外。一些类型的例子:jar,war,ejb-client和test-jar。如果设置extensions为 true,就可以在 plugin里定义新的类型。所以前面的类型的例子不完整。-->    
      <type>jar</type>    
      <!-- 依赖的分类器。分类器可以区分属于同一个POM,但不同构建方式的构件。分类器名被附加到文件名的版本号后面。例如,如果你想要构建两个单独的构件成 JAR,一个使用Java    4编译器,另一个使用Java 6编译器,你就可以使用分类器来生成两个单独的JAR构件。-->    
      <classifier></classifier>    
      <!--依赖范围。在项目发布过程中,帮助决定哪些构件被包括进来。欲知详情请参考依赖机制。    
        - compile :默认范围,用于编译      
        - provided:类似于编译,但支持你期待jdk或者容器提供,类似于classpath      
        - runtime: 在执行时需要使用      
        - test:    用于test任务时使用      
        - system: 需要外在提供相应的元素。通过systemPath来取得      
        - systemPath: 仅用于范围为system。提供相应的路径      
        - optional:   当项目自身被依赖时,标注依赖是否传递。用于连续依赖时使用-->     
      <scope>test</scope>       
      <!--仅供system范围使用。注意,不鼓励使用这个元素,并且在新的版本中该元素可能被覆盖掉。该元素为依赖规定了文件系统上的路径。需要绝对路径而不是相对路径。推荐使用属性匹配绝对路径,例{java.  home}。-->    
      <systemPath></systemPath>     
      <!--当计算传递依赖时, 从依赖构件列表里,列出被排除的依赖构件集。即告诉maven你只依赖指定的项目,不依赖项目的依赖。此元素主要用于解决版本冲突问题-->    
      <exclusions>    
        <exclusion>     
          <artifactId>spring-core</artifactId>     
          <groupId>org.springframework</groupId>     
        </exclusion>     
      </exclusions>       
      <!--可选依赖,如果你在项目B中把C依赖声明为可选,你就需要在依赖于B的项目(例如项目A)中显式的引用对C的依赖。可选依赖阻断依赖的传递性。-->     
      <optional>true</optional>    
    </dependency>
    ...    
  </dependencies>




  <!--模块(有时称作子项目) 被构建成项目的一部分。列出的每个模块元素是指向该模块的目录的相对路径-->
  <modules>  
      <module>account-email</module>  
      <module>account-persist</module> 
      ... 
  </modules>





  <scm>     
      <!--SCM的URL,该URL描述了版本库和如何连接到版本库。欲知详情,请看SCMs提供的URL格式和列表。该连接只读。-->     
      <connection>     
          scm:svn:http://svn.baidu.com/banseon/maven/banseon/banseon-maven2-trunk(dao-trunk)      
      </connection>     
      <!--给开发者使用的,类似connection元素。即该连接不仅仅只读-->    
      <developerConnection>     
          scm:svn:http://svn.baidu.com/banseon/maven/banseon/dao-trunk      
      </developerConnection>    
      <!--当前代码的标签,在开发阶段默认为HEAD-->    
      <tag/>           
      <!--指向项目的可浏览SCM库(例如ViewVC或者Fisheye)的URL。-->     
      <url>http://svn.baidu.com/banseon</url>     
  </scm> 
  

  <!--项目分发信息,在执行mvn deploy后表示要发布的位置。有了这些信息就可以把网站部署到远程服务器或者把构件部署到远程仓库。-->     
  <distributionManagement>    
    <!--部署项目产生的构件到远程仓库需要的信息-->    
      <repository>    
        <!--是分配给快照一个唯一的版本号(由时间戳和构建流水号)?还是每次都使用相同的版本号?参见repositories/repository元素-->    
        <uniqueVersion/>    
        <id>banseon-maven2</id>     
        <name>banseon maven2</name>     
          <url>file://${basedir}/target/deploy</url>     
          <layout/>    
      </repository>    
      <!--构件的快照部署到哪里?如果没有配置该元素,默认部署到repository元素配置的仓库,参见distributionManagement/repository元素-->     
      <snapshotRepository>    
        <uniqueVersion/>    
        <id>banseon-maven2</id>    
          <name>Banseon-maven2 Snapshot Repository</name>    
          <url>scp://svn.baidu.com/banseon:/usr/local/maven-snapshot</url>     
        <layout/>    
      </snapshotRepository>    
      <!--部署项目的网站需要的信息-->     
      <site>    
    <!--部署位置的唯一标识符,用来匹配站点和settings.xml文件里的配置-->     
        <id>banseon-site</id>     
          <!--部署位置的名称-->    
          <name>business api website</name>     
          <!--部署位置的URL,按protocol://hostname/path形式-->    
          <url>     
            scp://svn.baidu.com/banseon:/var/www/localhost/banseon-web      
          </url>     
      </site>    
      <!--项目下载页面的URL。如果没有该元素,用户应该参考主页。使用该元素的原因是:帮助定位那些不在仓库里的构件(由于license限制)。-->    
      <downloadUrl/>    
      <!--如果构件有了新的group ID和artifact ID(构件移到了新的位置),这里列出构件的重定位信息。-->    
      <relocation>    
        <!--构件新的group ID-->    
        <groupId/>    
        <!--构件新的artifact ID-->    
        <artifactId/>    
        <!--构件新的版本号-->    
        <version/>    
        <!--显示给用户的,关于移动的额外信息,例如原因。-->    
        <message/>    
      </relocation>    
      <!-- 给出该构件在远程仓库的状态。不得在本地项目中设置该元素,因为这是工具自动更新的。有效的值有:none(默认),converted(仓库管理员从 Maven 1  POM转换过来),partner(直接从伙伴Maven 2仓库同步过来),deployed(从Maven 2实例部 署),verified(被核实时正确的和最终的)。-->    
      <status/>           
  </distributionManagement>

  <build>
    <!--当项目没有规定目标(Maven2 叫做阶段)时的默认值-->
    <defaultGoal>install</defaultGoal>
    <!--build目标文件的存放目录,默认在 ${basedir}/target 目录-->
    <directory>${basedir}/target</directory>
    <finalName>${artifactId}-${version}</finalName>
    <filters>
      <filter>filters/filter1.properties</filter>
    </filters>

    <!--这个元素描述了项目相关的所有资源路径列表,例如和项目相关的属性文件,这些资源被包含在最终的打包文件里。-->    
    <resources>    
      <!--这个元素描述了项目相关或测试相关的所有资源路径-->    
      <resource>    
        <!-- 描述了资源的目标路径。该路径相对target/classes目录(例如${project.build.outputDirectory})。举个例 子,如果你想资源在特定的包里(   org.apache.maven.message,你就必须该元素设置为org/apache/maven /messages。然而,如果你只是想把资源放到源码目录结构里,就不需要该配置。-->    
        <targetPath/>    
        <!--是否使用参数值代替参数名。参数值取自properties元素或者文件里配置的属性,文件在filters元素里列出。-->    
        <filtering/>    
        <!--描述存放资源的目录,该路径相对POM路径-->    
        <directory/>    
        <!--包含的模式列表,例如**/*.xml.-->    
        <includes/>    
        <!--排除的模式列表,例如**/*.xml-->    
        <excludes/>    
      </resource>    
    </resources>    
    <!--这个元素描述了单元测试相关的所有资源路径,例如和单元测试相关的属性文件。-->    
    <testResources>    
      <!--这个元素描述了测试相关的所有资源路径,参见build/resources/resource元素的说明-->    
      <testResource>    
        <targetPath/>
        <filtering/>
        <directory/>
        <includes/>
        <excludes/>    
      </testResource>    
    </testResources>

    <plugins>
      <plugin>
          <groupId>org.apache.maven.plugins</groupId>
          <artifactId>maven-release-plugin</artifactId>
          <version>2.5.3</version>
          <configuration>
              <tagBase>${git.conn}</tagBase>
              <branchBase>${git.conn}</branchBase>
              <username>${git.username}</username>
              <password>${git.password}</password>
          </configuration>
      </plugin>
      ...
    </plugins>

    <!--子项目可以引用的默认插件信息。该插件配置项直到被引用时才会被解析或绑定到生命周期。给定插件的任何本地配置都会覆盖这里的配置-->
    <pluginManagement>
      <plugins>
        ...
      </plugins>
    </pluginManagement>

  </build>

</project>

二叉树查找树

1. 需求

  • 二叉树查找树(简单)

2. 设计

  • 结构
    1. 树,根节点和长度;节点,左右节点,值
  • 行为
    1. 插入,删除,查找
    2. 遍历:
      • 通用方案,每个节点都会入栈两次,对节点加状态用于判断是否打印,通过出栈顺序控制先中后序遍历
      • 变种通用,不更改节点或栈的数据结构,由于节点首次遍历都不会打印,可用map保存节点允许打印状态
  • 边界条件
    1. 删除的节点, 无子节点
    2. 删除的节点, 只有单个子节点
    3. 删除的节点,有双子节点,去左子树右下节点或者右子树左下节点替换

3. 代码

package tree

import (
	"container/list"
	"strconv"
	"strings"
)

type node struct {
	left, right *node
	Value       int
}

type BiTree struct {
	root *node
	len  int
}

func NewBiTree() *BiTree {
	return &BiTree{}
}

func (t *BiTree) Add(v int) {
	n := t.root
	if n == nil {
		t.root = &node{Value: v}
		t.len++
		return
	}
	for {
		switch {
		case v < n.Value:
			if n.left == nil {
				new := &node{Value: v}
				n.left = new
				t.len++
				return
			}
			n = n.left
		default:
			if n.right == nil {
				new := &node{Value: v}
				n.right = new
				t.len++
				return
			}
			n = n.right
		}
	}

}

func (t *BiTree) Delete(v int) *node {
	p, n := find(t.root, v)

	switch {
	case n == nil:
		return nil
	case n.left == nil || n.right == nil:
		a := If(n.left == nil, n.right, n.left)
		replace(p, n, a)
		return n
	default:
		maxP, max := findMax(n.left)
		replace(maxP, max, nil)
		if n != t.root {
			replace(p, n, max)
		} else {
			t.root = max
		}
		if n.left != max {
			max.left = n.left
		}
		max.right = n.right
		return n
	}
}

func replace(p, a, b *node) {
	switch {
	case p == nil:
		return
	case p.left == a:
		p.left = b
	default:
		p.right = b
	}
}

func If(condition bool, v1, v2 *node) *node {
	if condition {
		return v1
	}
	return v2
}

func findMax(root *node) (p, n *node) {
	n = root
	for {
		switch {
		case n == nil:
			return nil, nil
		case n.right == nil:
			return p, n
		default:
			p = n
			n = n.right
		}
	}
}
func find(root *node, v int) (p, n *node) {
	n = root
	for {
		switch {
		case n == nil:
			return nil, nil
		case v == n.Value:
			return p, n
		case v < n.Value:
			p = n
			n = n.left
			continue
		default:
			p = n
			n = n.right
			continue
		}
	}

}

func (t *BiTree) Search(v int) *node {
	_, n := find(t.root, v)
	return n
}

func (t *BiTree) Print(a int) string {

	if t.root == nil {
		return ""
	}
	data := make([]string, 0, t.len)
	s := &stack{list.New()}
	switch a {
	case 10:
		n := t.root
		m := make(map[*node]bool)
		s.push(t.root)
		for !s.empty() {
			n = s.pop()
			if n == nil {
				continue
			}
			if m[n] { //由于每个节点都会入栈两次,第一次标记,第二次打印值
				data = append(data, strconv.Itoa(n.Value))
			} else {
				s.push(n.right)
				s.push(n.left)
				s.push(n)
				m[n] = true
			}
		}
	case 20:
		n := t.root
		m := make(map[*node]bool)
		s.push(t.root)
		for !s.empty() {
			n = s.pop()
			if n == nil {
				continue
			}
			if m[n] {
				data = append(data, strconv.Itoa(n.Value))
			} else {
				s.push(n.right)
				s.push(n)
				s.push(n.left)
				m[n] = true
			}
		}
	case 30:
		n := t.root
		m := make(map[*node]bool)
		s.push(t.root)
		for !s.empty() {
			n = s.pop()
			if n == nil {
				continue
			}
			if m[n] {
				data = append(data, strconv.Itoa(n.Value))
			} else {
				s.push(n)
				s.push(n.right)
				s.push(n.left)
				m[n] = true
			}
		}

	case 11:
		//先序遍历方案1:
		//对每个节点压入栈,打印值,同样处理左孩子;
		//到达左下角后,栈弹出节点处理右孩子

		n := t.root
		for !s.empty() || n != nil { //如果栈为空并且节点为空,代表没有节点能够处理
			if n != nil { //节点不为空的话处理左孩子
				data = append(data, strconv.Itoa(n.Value))
				s.push(n)
				n = n.left
			} else { //节点为空的话 弹出栈顶节点,处理其右孩子
				n = s.pop().right
			}
		}
	case 12:
		//先序遍历方案2:
		//按照栈先入后出结构,对每个节点打印值,先压入右孩子,再压入左孩子
		//栈弹出各个节点依次处理

		n := t.root
		s.push(t.root)
		for !s.empty() {
			n = s.pop()
			data = append(data, strconv.Itoa(n.Value))
			if n.right != nil {
				s.push(n.right)
			}
			if n.left != nil {
				s.push(n.left)
			}
		}
	case 21:
		//中序遍历方案1:
		//对每个节点压入栈,同样处理左孩子;
		//到达左下角后,栈弹出节点,打印值,处理右孩子
		n := t.root
		for !s.empty() || n != nil {
			if n != nil {
				s.push(n)
				n = n.left
			} else {
				n = s.pop()
				data = append(data, strconv.Itoa(n.Value))
				n = n.right
			}
		}
	case 31:
		//后序遍历方案1:
		// 左右根 翻转是 根右左
		//按照栈先入后出结构,对每个节点打印值,先压入左孩子,再压入右孩子
		//栈弹出各个节点依次处理
		//最后翻转
		n := t.root
		s.push(t.root)
		for !s.empty() {
			n = s.pop()
			data = append(data, strconv.Itoa(n.Value))
			if n.left != nil {
				s.push(n.left)
			}
			if n.right != nil {
				s.push(n.right)
			}
		}
		for i, j := 0, len(data)-1; i < j; i, j = i+1, j-1 {
			data[i], data[j] = data[j], data[i]
		}

	case 4:
		n := t.root
		s.push(t.root)
		q := &queue{list.New()}
		q.put(t.root)
		for !q.empty() {
			n = q.poll()
			if n == nil {
				continue
			}
			data = append(data, strconv.Itoa(n.Value))
			q.put(n.left)
			q.put(n.right)
		}
	}
	return strings.Join(data, ",")
}

type stack struct {
	list *list.List
}

func (s *stack) push(v *node) {
	s.list.PushBack(v)
}

func (s *stack) empty() bool {
	return s.list.Len() == 0
}
func (s *stack) pop() *node {
	e := s.list.Back()
	if e != nil {
		s.list.Remove(e)
		return e.Value.(*node)
	}
	return nil
}

type queue struct {
	list *list.List
}

func (s *queue) put(v *node) {
	s.list.PushBack(v)
}

func (s *queue) empty() bool {
	return s.list.Len() == 0
}
func (s *queue) poll() *node {
	e := s.list.Front()
	if e != nil {
		s.list.Remove(e)
		return e.Value.(*node)
	}
	return nil
}

Java 日志简介2

1. 常见日志框架

image

2. slf4j

应用调了sl4j-api,即日志门面接口。

日志门面接口本身通常并没有实际的日志输出能力,它底层还是需要去调用具体的日志框架API的,也就是实际上它需要跟具体的日志框架结合使用。

由于具体日志框架比较多,而且互相也大都不兼容,日志门面接口要想实现与任意日志框架结合可能需要对应的桥接器,上图红框中的组件即是对应的各种桥接器!

image

3. 日志总结

1. log4j1

  • log4: log4j1的全部内容

2. log4j2

  • log4j-api: log4j2定义的API
  • log4j-core: log4j2上述API的实现

3. logback

  • logback-core: logback的核心包
  • logback-classic: logback实现了slf4j的API

4. commons-logging

  • commons-logging: commons-logging的原生全部内容
  • log4j-jcl: commons-logging到log4j2的桥梁
  • jcl-over-slf4j: commons-logging到slf4j的桥梁

5. slf4j桥接器

  • slf4j-jdk14:slf4j到jdk-logging的桥梁
  • slf4j-log4j12:slf4j到log4j1的桥梁
  • log4j-slf4j-impl:slf4j到log4j2的桥梁
  • logback-classic:slf4j到logback的桥梁
  • slf4j-jcl:slf4j到commons-logging的桥梁

6. 实际日志转向slf4j

  • jul-to-slf4j:jdk-logging到slf4j的桥梁
  • log4j-over-slf4j:log4j1到slf4j的桥梁
  • jcl-over-slf4j:commons-logging到slf4j的桥梁

4. 原理

1. slf4j日志绑定原理

  • 在classpath下查找org/slf4j/impl/StaticLoggerBinder.class(每个实现包中都会有)
  • 在编译期间,编译器会选择其中一个StaticLoggerBinder.class进行绑定(个人猜测classpath顺序,可以通过自定义org.slf4j.impl.StaticLoggerBinde指定)
  • 通过StaticLoggerBinder.class获取LoggerFactory

2. 日志切换slf4j原理

image

计算器

1. 需求

  • 只进行四则运算,支持使用 () 改变运算符优先级

2. 设计

  • 结构

    利用栈结构和后缀表达式来计算数学表达式

  • 行为

    1. 通过后缀表达式计算:遇到数字压栈,遇到运算符弹出栈顶两个数字计算

    2. 生成后缀表达式:栈存运算符

      • 遇到数字输出,

      • 遇到运算符,栈顶的优先级更低则入栈,更高或相等则输出

      • 遇到运算符,栈顶为直接入栈

      • 运算符为则全部弹出,直到遇到

      • 最后弹出所有栈内运算符

  • 边界条件

    1. 数字不止一位,通过逗号分隔

3. 代码

package main

import (
	"container/list"
	"fmt"
	"strconv"
	"unicode"
)

func main() {
	fmt.Println(cacl("1+22*3"))
}

func cacl(expr string) int {
	stack := &stack{list.New()}
	expr = in2post(expr)
	for i := 0; i < len(expr); i++ {
		s := string(expr[i])
		// 数字:直接压栈
		if unicode.IsDigit(rune(expr[i])) {
			j := i
			num := ""
			for ; j < len(expr) && unicode.IsDigit(rune(expr[j])); j++ { //获取整个数字
				num += string(expr[j])
			}
			stack.push(num)
			i = j //数字后都会跟有逗号,则跳过j-1
		} else {
			// 操作符:取出两个数字计算值,再将结果压栈
			num1, _ := strconv.Atoi(stack.pop())
			num2, _ := strconv.Atoi(stack.pop())
			switch s {
			case "+":
				stack.push(strconv.Itoa(num1 + num2))
			case "-":
				stack.push(strconv.Itoa(num1 - num2))
			case "*":
				stack.push(strconv.Itoa(num1 * num2))
			case "/":
				stack.push(strconv.Itoa(num1 / num2))
			}
		}
	}
	result, _ := strconv.Atoi(stack.top())
	return result
}

func in2post(expr string) (str string) {
	stack := stack{list.New()}
	for i := 0; i < len(expr); i++ {
		s := string(expr[i])
		switch s {
		case " ":
		case "(": //直接入栈
			stack.push(s)
		case ")": //弹出所有运算符,直至(
			for !stack.empty() {
				s = stack.pop()
				if s == "(" {
					break
				}
				str += s
			}
		case "0", "1", "2", "3", "4", "5", "6", "7", "8", "9": //数字直接输出
			j := i
			num := ""
			for ; j < len(expr) && unicode.IsDigit(rune(expr[j])); j++ { //获取整个数字
				num += string(expr[j])
			}
			str = str + num + ","
			i = j - 1
		default: //运算符:遇到更高级别运算符则弹出
			for !stack.empty() {
				top := stack.top()
				if top == "(" || isLower(top, s) {
					break
				}
				str += top
				stack.pop()
			}
			// 低优先级的运算符入栈
			stack.push(s)
		}
	}
	// 栈不空则全部输出
	for !stack.empty() {
		str += stack.pop()
	}

	return str
}

func isLower(top string, newTop string) bool {
	// 注意 a + b + c 的后缀表达式是 ab + c +,不是 abc + +
	switch top {
	case "+", "-":
		if newTop == "*" || newTop == "/" {
			return true
		}
	case "(":
		return true
	}
	return false
}

type stack struct {
	list *list.List
}

func (s *stack) push(v string) {
	s.list.PushBack(v)
}

func (s *stack) empty() bool {
	return s.list.Len() == 0
}
func (s *stack) top() string {
	return s.list.Back().Value.(string)
}

func (s *stack) pop() string {
	e := s.list.Back()
	if e != nil {
		s.list.Remove(e)
		return e.Value.(string)
	}
	return ""
}

队列

1. 需求

  • 队列(简单)

2. 设计

  • 结构
    1. 数据变动频繁,链表实现
    2. 方案二:使用类库中list实现,无容量限制(代码
  • 行为
    1. put,poll
  • 边界条件
    1. 队列满了
    2. 队列为空

3. 代码

package list

import (
	"container/list"
	"fmt"
	"strings"
)

type Queue struct {
	list *list.List
}

func NewQueue() *Queue {
	list := list.New()
	return &Queue{list}
}

func (q *Queue) Put(v interface{}) {
	q.list.PushBack(v)
}

func (q *Queue) Poll() interface{} {
	e := q.list.Front()
	if e != nil {
		q.list.Remove(e)
		return e.Value
	}
	return nil
}

func (q *Queue) Len() int {
	return q.list.Len()
}

func (q *Queue) String() (str string) {
	data := make([]string, 0, q.Len())
	for e := q.list.Front(); e != nil; e = e.Next() {
		data = append(data, fmt.Sprintf("%v", e.Value))
	}

	str = strings.Join(data, ",")
	str = "[" + str + "]"
	return
}

双向链表

1. 需求

  • 链表

2. 设计

  • 结构
    1. 双向链表,头节点,尾节点和长度;节点,数据和前后节点指针(代码
    2. 方案二:双向链表,root和len。root.pre为tail,tail.next为root
  • 行为
    1. 新增、删除、打印、获取、清空、包含
  • 边界条件
    1. 链表为空时新增
    2. 链表只有一个节点时删除
    3. 插入到头部

3. 代码

package list

import (
	"fmt"
	"strings"
)

type Node struct {
	next, pre *Node
	Value     interface{}
}

type LinkedList struct {
	root, tail *Node
	len        int
}

func NewLinkedList() *LinkedList {
	l := new(LinkedList)
	return l
}

func (l *LinkedList) Add(v interface{}) {
	if l.root == nil {
		l.root = &Node{Value: v}
		l.tail = l.root
		l.len++
		return
	}

	n := &Node{Value: v}
	n.pre = l.tail
	l.tail.next = n
	l.tail = n
	l.len++
}

func (l *LinkedList) Get(i int) interface{} {
	if i < 0 || i >= l.len {
		return nil
	}

	switch {
	case i == 0:
		return l.root.Value
	case i == l.len-1:
		return l.tail.Value
	case i < l.len/2: //下标小于长度1/2从头结点遍历
		n := l.root
		for x := 0; x < i-1; x++ {
			n = n.next
		}
		return n.Value
	default: //下标大于长度1/2从尾结点遍历
		n := l.tail
		for x := 0; x < l.len-1-i; x++ {
			n = n.pre
		}
		return n.Value
	}
}

func (l *LinkedList) Remove(i int) interface{} {
	if i < 0 || i >= l.len {
		return nil
	}

	switch {
	case l.len == 1: //长度为1代表只有头结点可删除
		n := l.root
		l.root = nil
		l.tail = nil
		l.len--
		return n.Value
	case i == 0:
		n := l.root
		l.root = l.root.next
		l.root.pre = nil
		l.len--
		return n.Value
	case i == l.len-1:
		n := l.tail
		l.tail = l.tail.pre
		l.tail.next = nil
		l.len--
		return n.Value
	case i < l.len/2: //下标小于长度1/2从头结点遍历
		n := l.root
		for x := 0; x < i-1; x++ {
			n = n.next
		}
		n.pre.next = n.next
		n.next.pre = n.pre
		l.len--
		return n.Value
	default: //下标大于长度1/2从尾结点遍历
		n := l.tail
		for x := 0; x < l.len-1-i; x++ {
			n = n.pre
		}
		n.pre.next = n.next
		n.next.pre = n.pre
		l.len--
		return n.Value
	}
}

func (l *LinkedList) Clear() {
	l.root = nil
	l.tail = nil
	l.len = 0
}
func (l *LinkedList) Contains(v interface{}) bool {
	n := l.root
	for n != nil {
		if n.Value == v {
			return true
		}
		n = n.next
	}
	return false
}

func (l *LinkedList) String() (str string) {
	data := make([]string, 0, l.len)
	n := l.root
	for n != nil {
		data = append(data, fmt.Sprintf("%v", n.Value))
		n = n.next
	}
	str = strings.Join(data, ",")
	str = "[" + str + "]"
	return
}

1. 需求

  • 栈(简单)

2. 设计

  • 结构
    1. 数据改动频繁,使用链表实现
    2. 方案二:使用container/list实现,无容量限制(代码
  • 行为
    1. push,pop,peak
  • 边界条件
    1. 栈满了,添加值
    2. 栈为空,获取值

3. 代码

package list

import (
	"container/list"
	"fmt"
	"strings"
)

type Stack struct {
	list *list.List
}

func NewStack() *Stack {
	list := list.New()
	return &Stack{list}
}

func (stack *Stack) Push(value interface{}) {
	stack.list.PushBack(value)
}

func (stack *Stack) Pop() interface{} {
	e := stack.list.Back()
	if e != nil {
		stack.list.Remove(e)
		return e.Value
	}
	return nil
}

func (stack *Stack) Peak() interface{} {
	e := stack.list.Back()
	if e != nil {
		return e.Value
	}
	return nil
}

func (stack *Stack) Len() int {
	return stack.list.Len()
}

func (stack *Stack) String() (str string) {
	data := make([]string, 0, stack.Len())
	for e := stack.list.Front(); e != nil; e = e.Next() {
		data = append(data, fmt.Sprintf("%v", e.Value))
	}

	str = strings.Join(data, ",")
	str = "[" + str + "]"
	return
}

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.