├── wechat-develop ├── src │ ├── main │ │ ├── resources │ │ │ └── application.yml │ │ └── java │ │ │ └── io │ │ │ └── github │ │ │ └── gozhuyinglong │ │ │ └── wechatdevelop │ │ │ ├── WechatDevelopApplication.java │ │ │ ├── config │ │ │ └── RestTemplateConfig.java │ │ │ ├── controller │ │ │ ├── AuthTestController.java │ │ │ └── WechatController.java │ │ │ ├── dto │ │ │ └── TokenDTO.java │ │ │ └── utils │ │ │ └── JwtUtil.java │ └── test │ │ └── java │ │ └── io │ │ └── github │ │ └── gozhuyinglong │ │ └── wechatdevelop │ │ └── WechatDevelopApplicationTests.java └── pom.xml ├── .gitignore ├── document └── qr_code.jpg ├── springboot-family ├── redis-template-fastjson │ ├── src │ │ ├── main │ │ │ ├── resources │ │ │ │ └── application.yml │ │ │ └── java │ │ │ │ └── io │ │ │ │ └── github │ │ │ │ └── gozhuyinglong │ │ │ │ ├── RedisTemplateFastjsonApplication.java │ │ │ │ ├── pojo │ │ │ │ └── Person.java │ │ │ │ └── config │ │ │ │ └── RedisConfig.java │ │ └── test │ │ │ └── java │ │ │ └── io │ │ │ └── github │ │ │ └── gozhuyinglong │ │ │ └── RedisTest.java │ └── pom.xml └── pom.xml ├── java-source-analysis ├── src │ └── main │ │ └── java │ │ └── io │ │ └── github │ │ └── gozhuyinglong │ │ ├── reflection │ │ ├── PersonEnum.java │ │ ├── PersonInterface.java │ │ ├── PersonParent.java │ │ ├── PersonAnnotation.java │ │ ├── Person.java │ │ └── ReflectionTest.java │ │ ├── proxy │ │ ├── Foo.java │ │ ├── RealFoo.java │ │ ├── MyInvocationHandler.java │ │ └── ProxyTest.java │ │ └── utils │ │ ├── CodeUtil.java │ │ ├── MDUtil.java │ │ ├── AsymmetricKeyUtil.java │ │ └── SymmetricKeyUtil.java └── pom.xml ├── design-patterns ├── src │ └── main │ │ └── java │ │ └── io │ │ └── github │ │ └── gozhuyinglong │ │ └── designpatterns │ │ ├── factory │ │ ├── FactoryAbstract.java │ │ ├── FactorySimple.java │ │ └── FactoryMethod.java │ │ ├── strategy │ │ ├── behavior │ │ │ ├── Fly.java │ │ │ ├── Quack.java │ │ │ ├── FlyNoWay.java │ │ │ ├── QuackNoWay.java │ │ │ ├── FlyWithWings.java │ │ │ ├── QuackGuaGua.java │ │ │ └── QuackZhiZhi.java │ │ ├── DecoyDuck.java │ │ ├── RubberDuck.java │ │ ├── MallardDuck.java │ │ ├── RedheadDuck.java │ │ ├── Duck.java │ │ └── Test.java │ │ └── singleton │ │ ├── SingletonEnum.java │ │ ├── SingletonEager.java │ │ ├── SingletonInnerClass.java │ │ ├── SingletonLazy.java │ │ ├── SingletonDCL.java │ │ └── SingletonTest.java └── pom.xml ├── spring-source-analysis ├── src │ └── main │ │ └── java │ │ └── io │ │ └── github │ │ └── gozhuyinglong │ │ └── importanalysis │ │ ├── config │ │ ├── ConfigA.java │ │ ├── ConfigC.java │ │ ├── ConfigD.java │ │ ├── ConfigB.java │ │ ├── MyImportSelector.java │ │ ├── MyImportBeanDefinitionRegistrar.java │ │ └── Config.java │ │ └── ImportDemo.java └── pom.xml ├── java-data-structures ├── pom.xml └── src │ └── main │ └── java │ └── io │ └── github │ └── gozhuyinglong │ └── datastructures │ ├── array │ ├── ArrayDemo.java │ └── SparseArrayDemo.java │ ├── stack │ ├── StackDemo.java │ └── StackDemoBalancedChar.java │ ├── tree │ ├── TreeDemo.java │ ├── BinaryTreeDemo.java │ ├── BinarySearchTreeDemo.java │ └── AVLTreeDemo.java │ ├── linkedlist │ ├── CircularLinkedListDemo.java │ ├── SinglyLinkedListDemo.java │ └── DoublyLinkedListDemo.java │ ├── queue │ └── ArrayQueueDemo.java │ └── hashtable │ └── HashTableDemo.java ├── netty-demo ├── pom.xml └── src │ └── main │ └── java │ └── io │ └── github │ └── gozhuyinglong │ ├── bio │ ├── BioClient.java │ └── BioServer.java │ ├── netty │ ├── server │ │ ├── NettyNioServerHandler.java │ │ └── NettyNioServer.java │ └── client │ │ ├── NettyNioClient.java │ │ └── NettyNioClientHandler.java │ ├── aio │ ├── AioClient.java │ └── AioServer.java │ └── nio │ ├── NioClient.java │ └── NioServer.java ├── README.md ├── python-spider └── area-number │ └── area_number_2020.py └── LICENSE /wechat-develop/src/main/resources/application.yml: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.idea 2 | *.iml 3 | **/target 4 | *out/ 5 | */.mvn 6 | -------------------------------------------------------------------------------- /document/qr_code.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gozhuyinglong/blog-demos/HEAD/document/qr_code.jpg -------------------------------------------------------------------------------- /springboot-family/redis-template-fastjson/src/main/resources/application.yml: -------------------------------------------------------------------------------- 1 | spring: 2 | redis: 3 | host: localhost 4 | port: 6379 5 | database: 0 6 | -------------------------------------------------------------------------------- /java-source-analysis/src/main/java/io/github/gozhuyinglong/reflection/PersonEnum.java: -------------------------------------------------------------------------------- 1 | package io.github.gozhuyinglong.reflection; 2 | 3 | /** 4 | * @author 码农StayUp 5 | * @date 2021/2/2 0002 6 | */ 7 | public enum PersonEnum { 8 | MAN, WOMAN 9 | } 10 | -------------------------------------------------------------------------------- /java-source-analysis/src/main/java/io/github/gozhuyinglong/proxy/Foo.java: -------------------------------------------------------------------------------- 1 | package io.github.gozhuyinglong.proxy; 2 | 3 | /** 4 | * @author 码农StayUp 5 | * @date 2021/2/22 0022 6 | */ 7 | public interface Foo { 8 | 9 | String ping(String name); 10 | 11 | } 12 | -------------------------------------------------------------------------------- /java-source-analysis/src/main/java/io/github/gozhuyinglong/reflection/PersonInterface.java: -------------------------------------------------------------------------------- 1 | package io.github.gozhuyinglong.reflection; 2 | 3 | /** 4 | * @author 码农StayUp 5 | * @date 2021/2/7 0007 6 | */ 7 | public interface PersonInterface { 8 | 9 | String sayHello(); 10 | } 11 | -------------------------------------------------------------------------------- /design-patterns/src/main/java/io/github/gozhuyinglong/designpatterns/factory/FactoryAbstract.java: -------------------------------------------------------------------------------- 1 | package io.github.gozhuyinglong.designpatterns.factory; 2 | 3 | /** 4 | * 抽象工厂模式 5 | * 6 | * @author 码农StayUp 7 | * @date 2020/12/9 0009 8 | */ 9 | public class FactoryAbstract { 10 | } 11 | -------------------------------------------------------------------------------- /design-patterns/src/main/java/io/github/gozhuyinglong/designpatterns/strategy/behavior/Fly.java: -------------------------------------------------------------------------------- 1 | package io.github.gozhuyinglong.designpatterns.strategy.behavior; 2 | 3 | /** 4 | * 飞行 5 | * 6 | * @author 码农StayUp 7 | * @date 2021/1/31 0031 8 | */ 9 | public interface Fly { 10 | 11 | void fly(); 12 | 13 | } 14 | -------------------------------------------------------------------------------- /design-patterns/src/main/java/io/github/gozhuyinglong/designpatterns/strategy/behavior/Quack.java: -------------------------------------------------------------------------------- 1 | package io.github.gozhuyinglong.designpatterns.strategy.behavior; 2 | 3 | /** 4 | * 叫声 5 | * 6 | * @author 码农StayUp 7 | * @date 2021/1/31 0031 8 | */ 9 | public interface Quack { 10 | 11 | void quack(); 12 | 13 | } 14 | -------------------------------------------------------------------------------- /spring-source-analysis/src/main/java/io/github/gozhuyinglong/importanalysis/config/ConfigA.java: -------------------------------------------------------------------------------- 1 | package io.github.gozhuyinglong.importanalysis.config; 2 | 3 | /** 4 | * @author 码农StayUp 5 | * @date 2021/1/28 0028 6 | */ 7 | public class ConfigA { 8 | 9 | public void print() { 10 | System.out.println("输出:ConfigA.class"); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /spring-source-analysis/src/main/java/io/github/gozhuyinglong/importanalysis/config/ConfigC.java: -------------------------------------------------------------------------------- 1 | package io.github.gozhuyinglong.importanalysis.config; 2 | 3 | /** 4 | * @author 码农StayUp 5 | * @date 2021/1/28 0028 6 | */ 7 | public class ConfigC { 8 | 9 | public void print() { 10 | System.out.println("输出:ConfigC.class"); 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /spring-source-analysis/src/main/java/io/github/gozhuyinglong/importanalysis/config/ConfigD.java: -------------------------------------------------------------------------------- 1 | package io.github.gozhuyinglong.importanalysis.config; 2 | 3 | /** 4 | * @author 码农StayUp 5 | * @date 2021/1/28 0028 6 | */ 7 | public class ConfigD { 8 | 9 | 10 | public void print() { 11 | System.out.println("输出:ConfigD.class"); 12 | } 13 | 14 | } 15 | -------------------------------------------------------------------------------- /java-source-analysis/src/main/java/io/github/gozhuyinglong/proxy/RealFoo.java: -------------------------------------------------------------------------------- 1 | package io.github.gozhuyinglong.proxy; 2 | 3 | /** 4 | * @author 码农StayUp 5 | * @date 2021/2/24 0024 6 | */ 7 | public class RealFoo implements Foo { 8 | 9 | @Override 10 | public String ping(String name) { 11 | System.out.println("ping"); 12 | return "pong"; 13 | } 14 | 15 | } 16 | -------------------------------------------------------------------------------- /wechat-develop/src/test/java/io/github/gozhuyinglong/wechatdevelop/WechatDevelopApplicationTests.java: -------------------------------------------------------------------------------- 1 | package io.github.gozhuyinglong.wechatdevelop; 2 | 3 | import org.junit.jupiter.api.Test; 4 | import org.springframework.boot.test.context.SpringBootTest; 5 | 6 | @SpringBootTest 7 | class WechatDevelopApplicationTests { 8 | 9 | @Test 10 | void contextLoads() { 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /design-patterns/src/main/java/io/github/gozhuyinglong/designpatterns/strategy/behavior/FlyNoWay.java: -------------------------------------------------------------------------------- 1 | package io.github.gozhuyinglong.designpatterns.strategy.behavior; 2 | 3 | /** 4 | * 不会飞行 5 | * 6 | * @author 码农StayUp 7 | * @date 2021/1/31 0031 8 | */ 9 | public class FlyNoWay implements Fly { 10 | @Override 11 | public void fly() { 12 | System.out.println("不会飞行"); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /design-patterns/src/main/java/io/github/gozhuyinglong/designpatterns/strategy/behavior/QuackNoWay.java: -------------------------------------------------------------------------------- 1 | package io.github.gozhuyinglong.designpatterns.strategy.behavior; 2 | 3 | /** 4 | * 不会叫 5 | * 6 | * @author 码农StayUp 7 | * @date 2021/1/31 0031 8 | */ 9 | public class QuackNoWay implements Quack { 10 | @Override 11 | public void quack() { 12 | System.out.println("不会叫"); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /design-patterns/src/main/java/io/github/gozhuyinglong/designpatterns/strategy/behavior/FlyWithWings.java: -------------------------------------------------------------------------------- 1 | package io.github.gozhuyinglong.designpatterns.strategy.behavior; 2 | 3 | /** 4 | * 用翅膀飞行 5 | * 6 | * @author 码农StayUp 7 | * @date 2021/1/31 0031 8 | */ 9 | public class FlyWithWings implements Fly { 10 | @Override 11 | public void fly() { 12 | System.out.println("用翅膀飞行"); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /design-patterns/src/main/java/io/github/gozhuyinglong/designpatterns/strategy/behavior/QuackGuaGua.java: -------------------------------------------------------------------------------- 1 | package io.github.gozhuyinglong.designpatterns.strategy.behavior; 2 | 3 | /** 4 | * 呱呱叫 5 | * 6 | * @author 码农StayUp 7 | * @date 2021/1/31 0031 8 | */ 9 | public class QuackGuaGua implements Quack { 10 | @Override 11 | public void quack() { 12 | System.out.println("呱呱叫"); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /design-patterns/src/main/java/io/github/gozhuyinglong/designpatterns/strategy/behavior/QuackZhiZhi.java: -------------------------------------------------------------------------------- 1 | package io.github.gozhuyinglong.designpatterns.strategy.behavior; 2 | 3 | /** 4 | * 吱吱叫 5 | * 6 | * @author 码农StayUp 7 | * @date 2021/1/31 0031 8 | */ 9 | public class QuackZhiZhi implements Quack { 10 | @Override 11 | public void quack() { 12 | System.out.println("吱吱叫"); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /design-patterns/src/main/java/io/github/gozhuyinglong/designpatterns/singleton/SingletonEnum.java: -------------------------------------------------------------------------------- 1 | package io.github.gozhuyinglong.designpatterns.singleton; 2 | 3 | /** 4 | * 单例模式 - 枚举类 5 | * 6 | * @author ZhuYinglong 7 | * @date 2020/12/7 0007 8 | */ 9 | public enum SingletonEnum { 10 | 11 | INSTANCE; 12 | 13 | public void method() { 14 | System.out.println("枚举类中定义方法!"); 15 | } 16 | 17 | } 18 | -------------------------------------------------------------------------------- /spring-source-analysis/src/main/java/io/github/gozhuyinglong/importanalysis/config/ConfigB.java: -------------------------------------------------------------------------------- 1 | package io.github.gozhuyinglong.importanalysis.config; 2 | 3 | import org.springframework.context.annotation.Configuration; 4 | 5 | /** 6 | * @author 码农StayUp 7 | * @date 2021/1/28 0028 8 | */ 9 | @Configuration 10 | public class ConfigB { 11 | 12 | public void print() { 13 | System.out.println("输出:ConfigB.class"); 14 | } 15 | 16 | } 17 | -------------------------------------------------------------------------------- /java-source-analysis/src/main/java/io/github/gozhuyinglong/reflection/PersonParent.java: -------------------------------------------------------------------------------- 1 | package io.github.gozhuyinglong.reflection; 2 | 3 | /** 4 | * @author 码农StayUp 5 | * @date 2021/2/7 0007 6 | */ 7 | public class PersonParent { 8 | 9 | protected PersonEnum sex; // 性别 10 | public String hobby; // 爱好 11 | 12 | public PersonEnum getSex() { 13 | return sex; 14 | } 15 | 16 | public void setSex(PersonEnum sex) { 17 | this.sex = sex; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /wechat-develop/src/main/java/io/github/gozhuyinglong/wechatdevelop/WechatDevelopApplication.java: -------------------------------------------------------------------------------- 1 | package io.github.gozhuyinglong.wechatdevelop; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | 6 | @SpringBootApplication 7 | public class WechatDevelopApplication { 8 | 9 | public static void main(String[] args) { 10 | SpringApplication.run(WechatDevelopApplication.class, args); 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /design-patterns/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | io.github.gozhuyinglong 8 | design-patterns 9 | 1.0-SNAPSHOT 10 | 11 | 12 | -------------------------------------------------------------------------------- /wechat-develop/src/main/java/io/github/gozhuyinglong/wechatdevelop/config/RestTemplateConfig.java: -------------------------------------------------------------------------------- 1 | package io.github.gozhuyinglong.wechatdevelop.config; 2 | 3 | import org.springframework.context.annotation.Bean; 4 | import org.springframework.context.annotation.Configuration; 5 | import org.springframework.web.client.RestTemplate; 6 | 7 | @Configuration 8 | public class RestTemplateConfig { 9 | 10 | @Bean 11 | public RestTemplate restTemplate() { 12 | return new RestTemplate(); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /java-source-analysis/src/main/java/io/github/gozhuyinglong/reflection/PersonAnnotation.java: -------------------------------------------------------------------------------- 1 | package io.github.gozhuyinglong.reflection; 2 | 3 | import java.lang.annotation.Retention; 4 | import java.lang.annotation.Target; 5 | 6 | import static java.lang.annotation.ElementType.*; 7 | import static java.lang.annotation.RetentionPolicy.RUNTIME; 8 | 9 | /** 10 | * @author 码农StayUp 11 | * @date 2021/2/2 0002 12 | */ 13 | @Retention(RUNTIME) 14 | @Target({TYPE, CONSTRUCTOR, METHOD, FIELD, PARAMETER}) 15 | public @interface PersonAnnotation { 16 | } 17 | -------------------------------------------------------------------------------- /springboot-family/redis-template-fastjson/src/main/java/io/github/gozhuyinglong/RedisTemplateFastjsonApplication.java: -------------------------------------------------------------------------------- 1 | package io.github.gozhuyinglong; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | 6 | /** 7 | * @author 码农StayUp 8 | * @date 2021/10/9 0009 9 | */ 10 | @SpringBootApplication 11 | public class RedisTemplateFastjsonApplication { 12 | 13 | public static void main(String[] args) { 14 | SpringApplication.run(RedisTemplateFastjsonApplication.class, args); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /spring-source-analysis/src/main/java/io/github/gozhuyinglong/importanalysis/config/MyImportSelector.java: -------------------------------------------------------------------------------- 1 | package io.github.gozhuyinglong.importanalysis.config; 2 | 3 | import org.springframework.context.annotation.ImportSelector; 4 | import org.springframework.core.type.AnnotationMetadata; 5 | 6 | /** 7 | * @author 码农StayUp 8 | * @date 2021/1/28 0028 9 | */ 10 | public class MyImportSelector implements ImportSelector { 11 | 12 | @Override 13 | public String[] selectImports(AnnotationMetadata importingClassMetadata) { 14 | return new String[]{"io.github.gozhuyinglong.importanalysis.config.ConfigC"}; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /spring-source-analysis/src/main/java/io/github/gozhuyinglong/importanalysis/ImportDemo.java: -------------------------------------------------------------------------------- 1 | package io.github.gozhuyinglong.importanalysis; 2 | 3 | import io.github.gozhuyinglong.importanalysis.config.Config; 4 | import org.springframework.context.annotation.AnnotationConfigApplicationContext; 5 | 6 | /** 7 | * @author 码农StayUp 8 | * @date 2021/1/28 0028 9 | */ 10 | public class ImportDemo { 11 | 12 | public static void main(String[] args) { 13 | AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(Config.class); 14 | Config config = ctx.getBean(Config.class); 15 | config.print(); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /design-patterns/src/main/java/io/github/gozhuyinglong/designpatterns/strategy/DecoyDuck.java: -------------------------------------------------------------------------------- 1 | package io.github.gozhuyinglong.designpatterns.strategy; 2 | 3 | import io.github.gozhuyinglong.designpatterns.strategy.behavior.FlyNoWay; 4 | import io.github.gozhuyinglong.designpatterns.strategy.behavior.QuackNoWay; 5 | 6 | /** 7 | * 诱饵鸭 8 | * 9 | * @author 码农StayUp 10 | * @date 2021/1/31 0031 11 | */ 12 | public class DecoyDuck extends Duck { 13 | 14 | // 诱饵鸭不会飞行,也不会叫 15 | public DecoyDuck() { 16 | this.fly = new FlyNoWay(); 17 | this.quack = new QuackNoWay(); 18 | } 19 | 20 | @Override 21 | public void display() { 22 | System.out.println("外观是诱饵鸭"); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /design-patterns/src/main/java/io/github/gozhuyinglong/designpatterns/strategy/RubberDuck.java: -------------------------------------------------------------------------------- 1 | package io.github.gozhuyinglong.designpatterns.strategy; 2 | 3 | import io.github.gozhuyinglong.designpatterns.strategy.behavior.FlyNoWay; 4 | import io.github.gozhuyinglong.designpatterns.strategy.behavior.QuackZhiZhi; 5 | 6 | /** 7 | * 橡皮鸭 8 | * 9 | * @author 码农StayUp 10 | * @date 2021/1/31 0031 11 | */ 12 | public class RubberDuck extends Duck { 13 | 14 | // 橡皮鸭不会飞行,吱吱叫 15 | public RubberDuck() { 16 | this.fly = new FlyNoWay(); 17 | this.quack = new QuackZhiZhi(); 18 | } 19 | 20 | @Override 21 | public void display() { 22 | System.out.println("外观是橡皮鸭"); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /design-patterns/src/main/java/io/github/gozhuyinglong/designpatterns/singleton/SingletonEager.java: -------------------------------------------------------------------------------- 1 | package io.github.gozhuyinglong.designpatterns.singleton; 2 | 3 | /** 4 | * 单例模式 - 饿汉式 5 | * 6 | * @author 码农StayUp 7 | * @date 2020/12/7 0007 8 | */ 9 | public class SingletonEager { 10 | 11 | /** 12 | * 私有实例,静态变量会在类加载的时候初始化,是线程安全的 13 | */ 14 | private static final SingletonEager instance = new SingletonEager(); 15 | 16 | /** 17 | * 私有构造方法 18 | */ 19 | private SingletonEager() { 20 | } 21 | 22 | /** 23 | * 唯一公开获取实例的方法(静态工厂方法) 24 | * 25 | * @return 26 | */ 27 | public static SingletonEager getInstance() { 28 | return instance; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /design-patterns/src/main/java/io/github/gozhuyinglong/designpatterns/strategy/MallardDuck.java: -------------------------------------------------------------------------------- 1 | package io.github.gozhuyinglong.designpatterns.strategy; 2 | 3 | import io.github.gozhuyinglong.designpatterns.strategy.behavior.FlyWithWings; 4 | import io.github.gozhuyinglong.designpatterns.strategy.behavior.QuackGuaGua; 5 | 6 | /** 7 | * 野鸭 8 | * 9 | * @author 码农StayUp 10 | * @date 2021/1/31 0031 11 | */ 12 | public class MallardDuck extends Duck { 13 | 14 | // 野鸭用翅膀飞行,呱呱叫 15 | public MallardDuck() { 16 | this.fly = new FlyWithWings(); 17 | this.quack = new QuackGuaGua(); 18 | } 19 | 20 | @Override 21 | public void display() { 22 | System.out.println("外观是绿头鸭"); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /design-patterns/src/main/java/io/github/gozhuyinglong/designpatterns/strategy/RedheadDuck.java: -------------------------------------------------------------------------------- 1 | package io.github.gozhuyinglong.designpatterns.strategy; 2 | 3 | import io.github.gozhuyinglong.designpatterns.strategy.behavior.FlyWithWings; 4 | import io.github.gozhuyinglong.designpatterns.strategy.behavior.QuackGuaGua; 5 | 6 | /** 7 | * 红头鸭 8 | * 9 | * @author 码农StayUp 10 | * @date 2021/1/31 0031 11 | */ 12 | public class RedheadDuck extends Duck { 13 | 14 | // 红头鸭用翅膀飞行,呱呱叫 15 | public RedheadDuck() { 16 | this.fly = new FlyWithWings(); 17 | this.quack = new QuackGuaGua(); 18 | } 19 | 20 | @Override 21 | public void display() { 22 | System.out.println("外观是红头鸭"); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /java-source-analysis/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | io.github.gozhuyinglong 8 | java-source-analysis 9 | 1.0-SNAPSHOT 10 | 11 | 12 | 13 | 14 | 15 | junit 16 | junit 17 | 4.13.1 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /java-data-structures/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | io.github.gozhuyinglong 8 | java-data-structures 9 | 1.0-SNAPSHOT 10 | 11 | 12 | 13 | com.alibaba 14 | fastjson 15 | 1.2.73 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /design-patterns/src/main/java/io/github/gozhuyinglong/designpatterns/strategy/Duck.java: -------------------------------------------------------------------------------- 1 | package io.github.gozhuyinglong.designpatterns.strategy; 2 | 3 | import io.github.gozhuyinglong.designpatterns.strategy.behavior.Fly; 4 | import io.github.gozhuyinglong.designpatterns.strategy.behavior.Quack; 5 | 6 | /** 7 | * 鸭子抽象类 8 | * 9 | * @author 码农StayUp 10 | * @date 2021/1/31 0031 11 | */ 12 | public abstract class Duck { 13 | 14 | protected Fly fly; 15 | protected Quack quack; 16 | 17 | public void swim() { 18 | System.out.println("正在游泳..."); 19 | } 20 | 21 | public abstract void display(); 22 | 23 | public Fly getFly() { 24 | return fly; 25 | } 26 | 27 | public Quack getQuack() { 28 | return quack; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /spring-source-analysis/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | io.github.gozhuyinglong 8 | spring-source-analysis 9 | 1.0-SNAPSHOT 10 | 11 | 12 | 13 | 14 | org.springframework 15 | spring-webmvc 16 | 5.3.3 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /design-patterns/src/main/java/io/github/gozhuyinglong/designpatterns/strategy/Test.java: -------------------------------------------------------------------------------- 1 | package io.github.gozhuyinglong.designpatterns.strategy; 2 | 3 | /** 4 | * @author 码农StayUp 5 | * @date 2021/1/31 0031 6 | */ 7 | public class Test { 8 | 9 | public static void main(String[] args) { 10 | MallardDuck mallardDuck = new MallardDuck(); 11 | mallardDuck.display(); 12 | mallardDuck.swim(); 13 | mallardDuck.getFly().fly(); 14 | mallardDuck.getQuack().quack(); 15 | 16 | System.out.println("-------------------"); 17 | 18 | DecoyDuck decoyDuck = new DecoyDuck(); 19 | decoyDuck.display(); 20 | decoyDuck.swim(); 21 | decoyDuck.getFly().fly(); 22 | decoyDuck.getQuack().quack(); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /design-patterns/src/main/java/io/github/gozhuyinglong/designpatterns/singleton/SingletonInnerClass.java: -------------------------------------------------------------------------------- 1 | package io.github.gozhuyinglong.designpatterns.singleton; 2 | 3 | /** 4 | * 单例模式 - 静态内部类 5 | * 6 | * @author ZhuYinglong 7 | * @date 2020/12/7 0007 8 | */ 9 | public class SingletonInnerClass { 10 | 11 | /** 12 | * 私有构造方法 13 | */ 14 | private SingletonInnerClass() { 15 | } 16 | 17 | /** 18 | * 唯一公开获取实例的方法(静态工厂方法) 19 | * 20 | * @return 21 | */ 22 | public static SingletonInnerClass getInstance() { 23 | return LazyHolder.INSTANCE; 24 | } 25 | 26 | /** 27 | * 私有静态内部类 28 | */ 29 | private static class LazyHolder { 30 | private static final SingletonInnerClass INSTANCE = new SingletonInnerClass(); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /design-patterns/src/main/java/io/github/gozhuyinglong/designpatterns/singleton/SingletonLazy.java: -------------------------------------------------------------------------------- 1 | package io.github.gozhuyinglong.designpatterns.singleton; 2 | 3 | /** 4 | * 单例模式 - 懒汉式 5 | * 6 | * @author 码农StayUp 7 | * @date 2020/12/7 0007 8 | */ 9 | public class SingletonLazy { 10 | 11 | /** 12 | * 私有实例 13 | */ 14 | private static SingletonLazy instance; 15 | 16 | /** 17 | * 私有构造方法 18 | */ 19 | private SingletonLazy() { 20 | } 21 | 22 | /** 23 | * 唯一公开获取实例的方法(静态工厂方法),该方法使用synchronized加锁,来保证线程安全性 24 | * 25 | * @return 26 | */ 27 | public static synchronized SingletonLazy getInstance() { 28 | if (instance == null) { 29 | instance = new SingletonLazy(); 30 | } 31 | return instance; 32 | } 33 | 34 | } 35 | -------------------------------------------------------------------------------- /wechat-develop/src/main/java/io/github/gozhuyinglong/wechatdevelop/controller/AuthTestController.java: -------------------------------------------------------------------------------- 1 | package io.github.gozhuyinglong.wechatdevelop.controller; 2 | 3 | import io.github.gozhuyinglong.wechatdevelop.utils.JwtUtil; 4 | import org.springframework.web.bind.annotation.GetMapping; 5 | import org.springframework.web.bind.annotation.RequestMapping; 6 | import org.springframework.web.bind.annotation.RestController; 7 | 8 | import javax.servlet.http.HttpServletRequest; 9 | 10 | @RestController 11 | @RequestMapping("/auth") 12 | public class AuthTestController { 13 | 14 | @GetMapping("/test") 15 | public void test(HttpServletRequest request) { 16 | String username = JwtUtil.getUsername(request); 17 | System.out.println("-------------------鉴权测试-------------------"); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /netty-demo/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | io.github.gozhuyinglong 8 | netty-demo 9 | 1.0-SNAPSHOT 10 | 11 | 12 | 16 | 17 | io.netty 18 | netty-all 19 | 4.1.65.Final 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /spring-source-analysis/src/main/java/io/github/gozhuyinglong/importanalysis/config/MyImportBeanDefinitionRegistrar.java: -------------------------------------------------------------------------------- 1 | package io.github.gozhuyinglong.importanalysis.config; 2 | 3 | import org.springframework.beans.factory.support.BeanDefinitionRegistry; 4 | import org.springframework.beans.factory.support.RootBeanDefinition; 5 | import org.springframework.context.annotation.ImportBeanDefinitionRegistrar; 6 | import org.springframework.core.type.AnnotationMetadata; 7 | 8 | /** 9 | * @author 码农StayUp 10 | * @date 2021/1/28 0028 11 | */ 12 | public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar { 13 | 14 | @Override 15 | public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) { 16 | registry.registerBeanDefinition("configD", new RootBeanDefinition(ConfigD.class)); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /java-source-analysis/src/main/java/io/github/gozhuyinglong/proxy/MyInvocationHandler.java: -------------------------------------------------------------------------------- 1 | package io.github.gozhuyinglong.proxy; 2 | 3 | import java.lang.reflect.InvocationHandler; 4 | import java.lang.reflect.Method; 5 | import java.util.Arrays; 6 | 7 | /** 8 | * @author 码农StayUp 9 | * @date 2021/2/24 0024 10 | */ 11 | public class MyInvocationHandler implements InvocationHandler { 12 | 13 | // 目标对象 14 | private final Object target; 15 | 16 | public MyInvocationHandler(Object target) { 17 | this.target = target; 18 | } 19 | 20 | 21 | @Override 22 | public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { 23 | System.out.println("proxy - " + proxy.getClass()); 24 | System.out.println("method - " + method); 25 | System.out.println("args - " + Arrays.toString(args)); 26 | return method.invoke(target, args); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /spring-source-analysis/src/main/java/io/github/gozhuyinglong/importanalysis/config/Config.java: -------------------------------------------------------------------------------- 1 | package io.github.gozhuyinglong.importanalysis.config; 2 | 3 | import org.springframework.context.annotation.Configuration; 4 | import org.springframework.context.annotation.Import; 5 | 6 | import javax.annotation.Resource; 7 | 8 | /** 9 | * @author 码农StayUp 10 | * @date 2021/1/28 0028 11 | */ 12 | @Configuration 13 | @Import({ConfigA.class, 14 | ConfigB.class, 15 | MyImportSelector.class, 16 | MyImportBeanDefinitionRegistrar.class}) 17 | public class Config { 18 | 19 | @Resource 20 | ConfigA configA; 21 | 22 | @Resource 23 | ConfigB configB; 24 | 25 | @Resource 26 | ConfigC configC; 27 | 28 | @Resource 29 | ConfigD configD; 30 | 31 | public void print() { 32 | configA.print(); 33 | configB.print(); 34 | configC.print(); 35 | configD.print(); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /design-patterns/src/main/java/io/github/gozhuyinglong/designpatterns/singleton/SingletonDCL.java: -------------------------------------------------------------------------------- 1 | package io.github.gozhuyinglong.designpatterns.singleton; 2 | 3 | /** 4 | * 单例模式 - 双重校验锁(double-checked-locking) 5 | * 6 | * @author 码农StayUp 7 | * @date 2020/12/7 0007 8 | */ 9 | public class SingletonDCL { 10 | 11 | /** 12 | * 私有实例,volatile修饰的变量是具有可见性的(即被一个线程修改后,其他线程立即可见) 13 | */ 14 | private volatile static SingletonDCL instance; 15 | 16 | /** 17 | * 私有构造方法 18 | */ 19 | private SingletonDCL() { 20 | } 21 | 22 | /** 23 | * 唯一公开获取实例的方法(静态工厂方法) 24 | * 25 | * @return 26 | */ 27 | public static SingletonDCL getInstance() { 28 | if (instance == null) { 29 | synchronized (SingletonDCL.class) { 30 | if (instance == null) { 31 | instance = new SingletonDCL(); 32 | } 33 | } 34 | } 35 | return instance; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /springboot-family/redis-template-fastjson/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | springboot-family 7 | io.github.gozhuyinglong 8 | 1.0-SNAPSHOT 9 | 10 | 4.0.0 11 | 12 | redis-template-fastjson 13 | 14 | 15 | 16 | 17 | 18 | org.springframework.boot 19 | spring-boot-starter-data-redis 20 | 21 | 22 | 23 | 24 | com.alibaba 25 | fastjson 26 | 1.2.73 27 | 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /springboot-family/redis-template-fastjson/src/main/java/io/github/gozhuyinglong/pojo/Person.java: -------------------------------------------------------------------------------- 1 | package io.github.gozhuyinglong.pojo; 2 | 3 | import java.io.Serializable; 4 | 5 | /** 6 | * @author ZhuYinglong 7 | * @date 2021/10/9 0009 8 | */ 9 | public class Person implements Serializable { 10 | 11 | /** 12 | * 主键 13 | */ 14 | private Long id; 15 | 16 | /** 17 | * 姓名 18 | */ 19 | private String name; 20 | 21 | /** 22 | * 年龄 23 | */ 24 | private Integer age; 25 | 26 | public Long getId() { 27 | return id; 28 | } 29 | 30 | public void setId(Long id) { 31 | this.id = id; 32 | } 33 | 34 | public String getName() { 35 | return name; 36 | } 37 | 38 | public void setName(String name) { 39 | this.name = name; 40 | } 41 | 42 | public Integer getAge() { 43 | return age; 44 | } 45 | 46 | public void setAge(Integer age) { 47 | this.age = age; 48 | } 49 | 50 | @Override 51 | public String toString() { 52 | return "Person{" + 53 | "id=" + id + 54 | ", name='" + name + '\'' + 55 | ", age=" + age + 56 | '}'; 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /springboot-family/redis-template-fastjson/src/test/java/io/github/gozhuyinglong/RedisTest.java: -------------------------------------------------------------------------------- 1 | package io.github.gozhuyinglong; 2 | 3 | import com.alibaba.fastjson.JSON; 4 | import com.alibaba.fastjson.JSONObject; 5 | import io.github.gozhuyinglong.pojo.Person; 6 | import org.junit.jupiter.api.Test; 7 | import org.springframework.beans.factory.annotation.Autowired; 8 | import org.springframework.boot.test.context.SpringBootTest; 9 | import org.springframework.data.redis.core.RedisTemplate; 10 | 11 | /** 12 | * @author 码农StayUp 13 | * @date 2021/10/9 0009 14 | */ 15 | @SpringBootTest 16 | public class RedisTest { 17 | 18 | @Autowired 19 | private RedisTemplate redisTemplate; 20 | 21 | @Test 22 | public void testSet() { 23 | Person person = new Person(); 24 | person.setId(1L); 25 | person.setName("码农StayUp"); 26 | person.setAge(18); 27 | redisTemplate.opsForValue().set("person", person); 28 | } 29 | 30 | @Test 31 | public void testGet() { 32 | Object o = redisTemplate.opsForValue().get("person"); 33 | System.out.println(o); 34 | // 转换为 Person 对象 35 | Person person = JSON.parseObject((String) o, Person.class); 36 | assert person != null; 37 | System.out.println(person.toString()); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /netty-demo/src/main/java/io/github/gozhuyinglong/bio/BioClient.java: -------------------------------------------------------------------------------- 1 | package io.github.gozhuyinglong.bio; 2 | 3 | import java.io.IOException; 4 | import java.io.OutputStream; 5 | import java.net.InetSocketAddress; 6 | import java.net.Socket; 7 | import java.util.Scanner; 8 | 9 | /** 10 | * @author 码农StayUp 11 | * @date 2021/4/6 0006 12 | */ 13 | public class BioClient { 14 | 15 | private static final String HOST = "127.0.0.1"; 16 | private static final int PORT = 8080; 17 | 18 | public static void main(String[] args) throws IOException { 19 | 20 | // 创建 Socket 21 | Socket socket = new Socket(); 22 | // 连接远程端点 23 | socket.connect(new InetSocketAddress(HOST, PORT)); 24 | System.out.printf("连接成功,主机:%s,端口:%s\n", HOST, PORT); 25 | // 获取输出流 26 | OutputStream outputStream = socket.getOutputStream(); 27 | // 获取控制输入内容 28 | Scanner scanner = new Scanner(System.in); 29 | while (true) { 30 | System.out.print("请输入:"); 31 | String content = scanner.nextLine(); 32 | if ("quit".equals(content)) { 33 | break; 34 | } 35 | // 将控制台内容写入输出流 36 | outputStream.write(content.getBytes()); 37 | } 38 | outputStream.close(); 39 | scanner.close(); 40 | socket.close(); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /wechat-develop/src/main/java/io/github/gozhuyinglong/wechatdevelop/dto/TokenDTO.java: -------------------------------------------------------------------------------- 1 | package io.github.gozhuyinglong.wechatdevelop.dto; 2 | 3 | 4 | import java.io.Serializable; 5 | 6 | /** 7 | * @author ZhuYinglong 8 | * @date 2021/11/10 0010 9 | */ 10 | public class TokenDTO implements Serializable { 11 | 12 | /** 13 | * 生成的Token 14 | */ 15 | private String token; 16 | 17 | /** 18 | * 请将 Token 放入此请求头中 19 | */ 20 | private String tokenHeader; 21 | 22 | /** 23 | * 请为 Token 附加上此前缀 24 | */ 25 | private String tokenPrefix; 26 | 27 | /** 28 | * Token有效期,Unix毫秒数 29 | */ 30 | private Long ttl; 31 | 32 | public String getToken() { 33 | return token; 34 | } 35 | 36 | public void setToken(String token) { 37 | this.token = token; 38 | } 39 | 40 | public String getTokenHeader() { 41 | return tokenHeader; 42 | } 43 | 44 | public void setTokenHeader(String tokenHeader) { 45 | this.tokenHeader = tokenHeader; 46 | } 47 | 48 | public String getTokenPrefix() { 49 | return tokenPrefix; 50 | } 51 | 52 | public void setTokenPrefix(String tokenPrefix) { 53 | this.tokenPrefix = tokenPrefix; 54 | } 55 | 56 | public Long getTtl() { 57 | return ttl; 58 | } 59 | 60 | public void setTtl(Long ttl) { 61 | this.ttl = ttl; 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /netty-demo/src/main/java/io/github/gozhuyinglong/netty/server/NettyNioServerHandler.java: -------------------------------------------------------------------------------- 1 | package io.github.gozhuyinglong.netty.server; 2 | 3 | import io.netty.buffer.ByteBuf; 4 | import io.netty.channel.ChannelHandlerContext; 5 | import io.netty.channel.ChannelInboundHandlerAdapter; 6 | import io.netty.util.ReferenceCountUtil; 7 | 8 | import java.nio.charset.StandardCharsets; 9 | 10 | /** 11 | * @author 码农StayUp 12 | * @date 2021/11/23 0023 13 | */ 14 | public class NettyNioServerHandler extends ChannelInboundHandlerAdapter { 15 | 16 | private int count = 0; 17 | 18 | @Override 19 | public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { 20 | // 接收到的消息 21 | ByteBuf in = (ByteBuf) msg; 22 | try { 23 | byte[] b = new byte[in.readableBytes()]; 24 | in.readBytes(b); 25 | System.out.println("------------" + (++count)); 26 | System.out.println(new String(b, StandardCharsets.UTF_8)); 27 | } finally { 28 | // 显式的释放消息 29 | ReferenceCountUtil.release(msg); 30 | } 31 | } 32 | 33 | @Override 34 | public void channelReadComplete(ChannelHandlerContext ctx) throws Exception { 35 | ctx.flush(); 36 | } 37 | 38 | @Override 39 | public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { 40 | ctx.close(); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /springboot-family/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | io.github.gozhuyinglong 8 | springboot-family 9 | 1.0-SNAPSHOT 10 | pom 11 | 12 | springboot-family 13 | SpringBoot全家桶 14 | 15 | 16 | 1.8 17 | 18 | 19 | 20 | spring-boot-starter-parent 21 | org.springframework.boot 22 | 2.5.1 23 | 24 | 25 | 26 | 27 | 28 | org.springframework.boot 29 | spring-boot-starter-test 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | org.springframework.boot 38 | spring-boot-maven-plugin 39 | 40 | 41 | 42 | 43 | 44 | redis-template-fastjson 45 | 46 | 47 | -------------------------------------------------------------------------------- /netty-demo/src/main/java/io/github/gozhuyinglong/aio/AioClient.java: -------------------------------------------------------------------------------- 1 | package io.github.gozhuyinglong.aio; 2 | 3 | import java.net.InetSocketAddress; 4 | import java.nio.ByteBuffer; 5 | import java.nio.channels.AsynchronousSocketChannel; 6 | import java.util.Scanner; 7 | 8 | /** 9 | * @author 码农StayUp 10 | * @date 2021/4/22 0022 11 | */ 12 | public class AioClient { 13 | 14 | private static final String HOST = "127.0.0.1"; 15 | private static final int PORT = 8080; 16 | 17 | public static void main(String[] args) throws Exception { 18 | // 打开一个异步的 Socket 通道 19 | AsynchronousSocketChannel socketChannel = AsynchronousSocketChannel.open(); 20 | // 连接指定的服务端,并同步获取连接结果。get()操作会阻塞,直到连接成功。 21 | socketChannel.connect(new InetSocketAddress(HOST, PORT)).get(); 22 | // 申请一个1024字节的缓冲区 23 | ByteBuffer byteBuffer = ByteBuffer.allocate(1024); 24 | 25 | // 获取控制台输入内容,输入 quit 会退出输入,并关闭通道 26 | Scanner scanner = new Scanner(System.in); 27 | while (true) { 28 | System.out.print("请输入:"); 29 | String content = scanner.nextLine(); 30 | if ("quit".equals(content)) { 31 | break; 32 | } 33 | // 将控制台输入的内容写入缓冲区 34 | byteBuffer.put(content.getBytes()); 35 | // 反转缓冲区(从写入变为读取) 36 | byteBuffer.flip(); 37 | // 将缓冲区中的内容写入到 Socket 通道中。get()操作会阻塞,直到写入成功。 38 | socketChannel.write(byteBuffer).get(); 39 | // 清除缓冲区 40 | byteBuffer.clear(); 41 | } 42 | scanner.close(); 43 | socketChannel.close(); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /netty-demo/src/main/java/io/github/gozhuyinglong/nio/NioClient.java: -------------------------------------------------------------------------------- 1 | package io.github.gozhuyinglong.nio; 2 | 3 | import java.io.IOException; 4 | import java.net.InetSocketAddress; 5 | import java.nio.ByteBuffer; 6 | import java.nio.channels.SocketChannel; 7 | import java.util.Scanner; 8 | 9 | /** 10 | * @author 码农StayUp 11 | * @date 2021/4/16 0016 12 | */ 13 | public class NioClient { 14 | 15 | private static final String HOST = "127.0.0.1"; 16 | private static final int PORT = 8080; 17 | 18 | public static void main(String[] args) throws IOException { 19 | 20 | // 打开一个 Socket 通道,并绑定指定的服务端地址与端口 21 | SocketChannel socketChannel = SocketChannel.open(); 22 | // 将该通道设为非阻塞 23 | socketChannel.configureBlocking(false); 24 | // 连接远程地址 25 | socketChannel.connect(new InetSocketAddress(HOST, PORT)); 26 | 27 | // 申请一个1024字节的缓冲区 28 | ByteBuffer byteBuffer = ByteBuffer.allocate(1024); 29 | // 获取控制台输入内容 30 | Scanner scanner = new Scanner(System.in); 31 | while (true) { 32 | System.out.print("请输入:"); 33 | String content = scanner.nextLine(); 34 | if ("quit".equals(content)) { 35 | break; 36 | } 37 | // 将控制台输入的内容写入缓冲区 38 | byteBuffer.put(content.getBytes()); 39 | // 反转缓冲区(从写入变为读取) 40 | byteBuffer.flip(); 41 | // 将缓冲区中的内容写入到 Socket 通道中 42 | socketChannel.write(byteBuffer); 43 | // 清除缓冲区 44 | byteBuffer.clear(); 45 | } 46 | scanner.close(); 47 | socketChannel.close(); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /java-source-analysis/src/main/java/io/github/gozhuyinglong/proxy/ProxyTest.java: -------------------------------------------------------------------------------- 1 | package io.github.gozhuyinglong.proxy; 2 | 3 | import org.junit.Test; 4 | 5 | import java.lang.reflect.InvocationHandler; 6 | import java.lang.reflect.Proxy; 7 | 8 | /** 9 | * @author 码农StayUp 10 | * @date 2021/2/24 0024 11 | */ 12 | public class ProxyTest { 13 | 14 | @Test 15 | public void test1() throws Exception { 16 | Foo foo = new RealFoo(); 17 | // 根据类加载器和接口数组获取代理类的Class对象 18 | Class proxyClass = Proxy.getProxyClass(Foo.class.getClassLoader(), Foo.class); 19 | 20 | // 通过Class对象的构造器创建一个实例(代理类的实例) 21 | Foo fooProxy = (Foo) proxyClass.getConstructor(InvocationHandler.class) 22 | .newInstance(new MyInvocationHandler(foo)); 23 | 24 | // 调用 ping 方法,并输出返回值 25 | String value = fooProxy.ping("杨过"); 26 | System.out.println(value); 27 | 28 | } 29 | 30 | @Test 31 | public void test2() { 32 | Foo foo = new RealFoo(); 33 | // 通过类加载器、接口数组和调用处理器,创建代理类的实例 34 | Foo fooProxy = (Foo) Proxy.newProxyInstance(Foo.class.getClassLoader(), 35 | new Class[]{Foo.class}, 36 | new MyInvocationHandler(foo)); 37 | String value = fooProxy.ping("小龙女"); 38 | System.out.println(value); 39 | } 40 | 41 | @Test 42 | public void test3() { 43 | Foo foo = new RealFoo(); 44 | 45 | Foo fooProxy = (Foo) Proxy.newProxyInstance(Foo.class.getClassLoader(), 46 | new Class[]{Foo.class}, 47 | (proxy, method, args) -> method.invoke(foo, args)); 48 | String value = fooProxy.ping("雕兄"); 49 | System.out.println(value); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /java-source-analysis/src/main/java/io/github/gozhuyinglong/reflection/Person.java: -------------------------------------------------------------------------------- 1 | package io.github.gozhuyinglong.reflection; 2 | 3 | /** 4 | * @author 码农StayUp 5 | * @date 2021/2/2 0002 6 | */ 7 | @PersonAnnotation 8 | public final class Person extends PersonParent implements PersonInterface { 9 | 10 | @PersonAnnotation 11 | private String name; // 姓名 12 | private int age; // 年龄 13 | public int height; // 身高 cm 14 | 15 | public Person() { 16 | } 17 | 18 | private Person(String name) { 19 | this.name = name; 20 | } 21 | 22 | public Person(String name, int age) { 23 | this.name = name; 24 | this.age = age; 25 | } 26 | 27 | @PersonAnnotation 28 | public Person(@PersonAnnotation String name, int age, PersonEnum sex) { 29 | this.name = name; 30 | this.age = age; 31 | this.sex = sex; 32 | } 33 | 34 | 35 | public String getName() { 36 | return name; 37 | } 38 | 39 | @PersonAnnotation 40 | public void setName(String name) { 41 | this.name = name; 42 | } 43 | 44 | public int getAge() { 45 | return age; 46 | } 47 | 48 | public void setAge(int age) { 49 | this.age = age; 50 | } 51 | 52 | private String display() { 53 | return "我叫" + name + ",今年" + age + "岁了!"; 54 | } 55 | 56 | 57 | @Override 58 | public String sayHello() { 59 | return "Hello!我叫[" + name + "]"; 60 | } 61 | 62 | @Override 63 | public String toString() { 64 | return "Person{" + 65 | "name='" + name + '\'' + 66 | ", age=" + age + 67 | ", sex='" + sex + '\'' + 68 | '}'; 69 | } 70 | 71 | } 72 | -------------------------------------------------------------------------------- /netty-demo/src/main/java/io/github/gozhuyinglong/netty/client/NettyNioClient.java: -------------------------------------------------------------------------------- 1 | package io.github.gozhuyinglong.netty.client; 2 | 3 | import io.netty.bootstrap.Bootstrap; 4 | import io.netty.channel.ChannelFuture; 5 | import io.netty.channel.ChannelInitializer; 6 | import io.netty.channel.nio.NioEventLoopGroup; 7 | import io.netty.channel.socket.SocketChannel; 8 | import io.netty.channel.socket.nio.NioSocketChannel; 9 | 10 | /** 11 | * @author 码农StayUp 12 | * @date 2021/11/23 0023 13 | */ 14 | public class NettyNioClient { 15 | 16 | private final String host; 17 | private final int port; 18 | 19 | public NettyNioClient(String host, int port) { 20 | this.host = host; 21 | this.port = port; 22 | } 23 | 24 | public void connect() throws Exception { 25 | NioEventLoopGroup group = new NioEventLoopGroup(); 26 | try { 27 | Bootstrap b = new Bootstrap(); 28 | b.group(group) 29 | .channel(NioSocketChannel.class) 30 | .handler(new ChannelInitializer() { 31 | @Override 32 | protected void initChannel(SocketChannel ch) throws Exception { 33 | ch.pipeline().addLast(new NettyNioClientHandler()); 34 | } 35 | }); 36 | // 开始连接 37 | ChannelFuture channelFuture = b.connect(host, port).sync(); 38 | // 阻塞住,直到套接字关闭。 39 | channelFuture.channel().closeFuture().sync(); 40 | } finally { 41 | group.shutdownGracefully(); 42 | } 43 | } 44 | 45 | public static void main(String[] args) throws Exception { 46 | new NettyNioClient("127.0.0.1", 8080).connect(); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /design-patterns/src/main/java/io/github/gozhuyinglong/designpatterns/singleton/SingletonTest.java: -------------------------------------------------------------------------------- 1 | package io.github.gozhuyinglong.designpatterns.singleton; 2 | 3 | import java.util.concurrent.TimeUnit; 4 | 5 | /** 6 | * 使用多线程测试单例模式 7 | * 8 | * @author ZhuYinglong 9 | * @date 2020/12/7 0007 10 | */ 11 | public class SingletonTest { 12 | 13 | public static void main(String[] args) throws InterruptedException { 14 | 15 | final int loop = 5; 16 | 17 | // 1.懒汉式 18 | for (int i = 0; i < loop; i++) { 19 | new Thread(() -> { 20 | SingletonLazy singleton = SingletonLazy.getInstance(); 21 | System.out.println("1.懒汉式:" + singleton.hashCode()); 22 | }).start(); 23 | } 24 | 25 | // 2.饿汉式 26 | for (int i = 0; i < loop; i++) { 27 | new Thread(() -> { 28 | SingletonEager singleton = SingletonEager.getInstance(); 29 | System.out.println("2.饿汉式:" + singleton.hashCode()); 30 | }).start(); 31 | } 32 | 33 | // 3.双重校验锁 34 | for (int i = 0; i < loop; i++) { 35 | new Thread(() -> { 36 | SingletonDCL singleton = SingletonDCL.getInstance(); 37 | System.out.println("3.双重校验锁:" + singleton.hashCode()); 38 | }).start(); 39 | } 40 | 41 | // 4.静态内部类 42 | for (int i = 0; i < loop; i++) { 43 | new Thread(() -> { 44 | SingletonInnerClass singleton = SingletonInnerClass.getInstance(); 45 | System.out.println("4.静态内部类:" + singleton.hashCode()); 46 | }).start(); 47 | } 48 | 49 | // 5.枚举类 50 | for (int i = 0; i < loop; i++) { 51 | new Thread(() -> { 52 | SingletonEnum singleton = SingletonEnum.INSTANCE; 53 | System.out.println("5.枚举类:" + singleton.hashCode()); 54 | }).start(); 55 | } 56 | 57 | TimeUnit.SECONDS.sleep(1); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /wechat-develop/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | org.springframework.boot 7 | spring-boot-starter-parent 8 | 2.5.6 9 | 10 | 11 | io.github.gozhuyinglong 12 | wechat-develop 13 | 0.0.1-SNAPSHOT 14 | wechat-develop 15 | wechat-develop 16 | 17 | 1.8 18 | 19 | 20 | 21 | org.springframework.boot 22 | spring-boot-starter-web 23 | 24 | 25 | 26 | org.springframework.boot 27 | spring-boot-starter-test 28 | test 29 | 30 | 31 | 32 | com.alibaba 33 | fastjson 34 | 1.2.78 35 | 36 | 37 | 38 | io.jsonwebtoken 39 | jjwt 40 | 0.9.1 41 | 42 | 43 | 44 | 45 | 46 | 47 | org.springframework.boot 48 | spring-boot-maven-plugin 49 | 50 | 51 | 52 | 53 | 54 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## 关于本代码仓库 2 | 1. 主页地址:[https://gozhuyinglong.github.io](https://gozhuyinglong.github.io) 3 | 2. 这个代码仓库里是博客中涉及的源码、文件等 4 | 3. 如果对您有帮助,请给个Star,谢谢您! 5 | 6 | ![](document/qr_code.jpg) 7 | 8 | ## 数据结构与算法 9 | 10 | * [数组](https://mp.weixin.qq.com/s/YVbahU_0fzmyEX-JBvcnqQ) 11 | * [稀疏数组](https://mp.weixin.qq.com/s/YYemaomm10HiKs9MoKHKIw) 12 | * [链表(单链表、双链表、环形链表)](https://mp.weixin.qq.com/s/46ShChMslDGsV6xSObh5nQ) 13 | * [栈](https://mp.weixin.qq.com/s/dfv4WM_-agLpygCuzqQUTA) 14 | * [队列](https://mp.weixin.qq.com/s/64oTQJatNcBsfvrJKMQOWA) 15 | * [树](https://mp.weixin.qq.com/s/Ui5p4RQRwEHv4a_HWeXJYQ) 16 | * [二叉树](https://mp.weixin.qq.com/s/XkeEyUCCvQ_AtMLBUYTH0Q) 17 | * [二叉查找树(BST)](https://mp.weixin.qq.com/s/6S8M6r-EY4IMF3UUvZ7_AA) 18 | * [AVL树(平衡二叉树)](https://mp.weixin.qq.com/s/eeXi_11illdVqMnkse_mhQ) 19 | * [B树](https://mp.weixin.qq.com/s/Cx03l-ezvYjAKrmedup-aQ) 20 | * [散列表(哈希表)](https://mp.weixin.qq.com/s/oX28uyCbbaYQErT6RE-txg) 21 | 22 | ## 设计模式 23 | 24 | * [单例模式](https://mp.weixin.qq.com/s/bb2LhnCDUZfprHwLtAK18Q) 25 | * [简单工厂模式](https://mp.weixin.qq.com/s/tS_m1_8E0wn24UNkHTXeug) 26 | * [策略模式](https://mp.weixin.qq.com/s/FfhMIrD72vBWTGJe5yJzxw) 27 | 28 | ## 码农工具箱 29 | 30 | * [教你使用GitHub搭建个人网站](https://mp.weixin.qq.com/s/fFP3sk8gaeG10dfZdPj4bQ) 31 | * [如何将代码同时提交到Github和码云Gitee上](https://mp.weixin.qq.com/s/7xvtYbW_U73QbAVW_4wCSw) 32 | * [80行Python代码搞定全国区划代码](https://mp.weixin.qq.com/s/RrryeSKCAwD61NHfjaFOrA) 33 | 34 | ## Java源码解析 35 | 36 | * [Java反射机制:跟着代码学反射](https://mp.weixin.qq.com/s/-JfevVj0xVHBAZ_AgowZAQ) 37 | * [JDK动态代理:不仅要学会用,更要掌握其原理](https://mp.weixin.qq.com/s/0M7ENqhZ2IjmPeFbf_vEqQ) 38 | 39 | ## Spring源码解析 40 | 41 | * [@Import注解:导入配置类的四种方式&源码解析](https://mp.weixin.qq.com/s/DcWEo6-7-W1yFpEdkcwIJQ) 42 | 43 | ## Netty系列 44 | 45 | * [75张图带你了解网络设备、网络地址规划、静态路由、实战演练](https://mp.weixin.qq.com/s/9McysTuIFQ984Asy2FxB9g) 46 | * [36张图详解网络基础知识](https://mp.weixin.qq.com/s/H7FQXsPxtHcYUzK-cHBo9g) 47 | * [浅聊Linux的五种IO模型](https://mp.weixin.qq.com/s/IrY6u8CIkYN2Rv-kKeMtMA) 48 | 49 | ## 信息安全 50 | 51 | * [一文搞懂单向散列函数](https://mp.weixin.qq.com/s/LrhMAXfxhnlPLxv9B_39sg) 52 | * [一文搞懂对称加密:加密算法、工作模式、填充方式、代码实现](https://mp.weixin.qq.com/s/Jr3aKhd9NEIZ7quWmBJAow) -------------------------------------------------------------------------------- /java-data-structures/src/main/java/io/github/gozhuyinglong/datastructures/array/ArrayDemo.java: -------------------------------------------------------------------------------- 1 | package io.github.gozhuyinglong.datastructures.array; 2 | 3 | import java.util.Arrays; 4 | 5 | /** 6 | * 数组 7 | * 8 | * @author 码农StayUp 9 | * @date 2020/11/15 0015 10 | */ 11 | public class ArrayDemo { 12 | 13 | public static void main(String[] args) { 14 | String[] array = new String[]{"a", "b", "c", "d"}; 15 | 16 | // 在指定下标处插入数据 17 | String[] insertArray = insert(array, 2, "m"); 18 | for (String arr : insertArray) { 19 | System.out.print(arr); 20 | } 21 | System.out.println(); 22 | 23 | // 删除指定下标的数据 24 | String[] removeArray = remove(array, 1); 25 | for (String arr : removeArray) { 26 | System.out.print(arr); 27 | } 28 | 29 | } 30 | 31 | /** 32 | * 在指定下标处插入数据 33 | * 34 | * @param array 35 | * @param index 36 | * @param data 37 | * @return 38 | */ 39 | public static String[] insert(String[] array, int index, String data) { 40 | // 判断参数是否合法 41 | if (array == null || array.length == 0 || index < 0 || index > array.length) { 42 | throw new RuntimeException("参数不合法!"); 43 | } 44 | // 复制出一个长度+1的数组 45 | String[] newArray = Arrays.copyOf(array, array.length + 1); 46 | // 将数据从index开始往后移1位;若index为最后一位,则无需移动 47 | if (array.length > index) { 48 | System.arraycopy(newArray, index, newArray, index + 1, newArray.length - index - 1); 49 | } 50 | // 将值放到index下标上 51 | newArray[index] = data; 52 | return newArray; 53 | } 54 | 55 | /** 56 | * 删除指定下标的数据 57 | * 58 | * @param array 59 | * @param index 60 | * @return 61 | */ 62 | public static String[] remove(String[] array, int index) { 63 | // 判断参数是否合法 64 | if (array == null || index < 0 || index >= array.length) { 65 | throw new RuntimeException("参数不合法!"); 66 | } 67 | // 将数据从index+1处往前移一位 68 | System.arraycopy(array, index + 1, array, index, array.length - index - 1); 69 | // 复制出长度-1的数组 70 | return Arrays.copyOf(array, array.length - 1); 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /netty-demo/src/main/java/io/github/gozhuyinglong/bio/BioServer.java: -------------------------------------------------------------------------------- 1 | package io.github.gozhuyinglong.bio; 2 | 3 | 4 | import java.io.IOException; 5 | import java.io.InputStream; 6 | import java.net.InetSocketAddress; 7 | import java.net.ServerSocket; 8 | import java.net.Socket; 9 | 10 | /** 11 | * @author 码农StayUp 12 | * @date 2021/4/6 0006 13 | */ 14 | public class BioServer { 15 | 16 | private static final int PORT = 8080; 17 | 18 | public static void main(String[] args) throws IOException { 19 | 20 | // 为服务端创建 ServerSocket 21 | ServerSocket serverSocket = new ServerSocket(); 22 | // 绑定本地端口 23 | serverSocket.bind(new InetSocketAddress(PORT)); 24 | System.out.printf("[%s] - 服务端启动了,端口为:%s\n", Thread.currentThread().getName(), PORT); 25 | 26 | // 循环接收每一个客户端连接,当没有连接时会阻塞 27 | while (true) { 28 | // 监听,等待客户端连接。该方法会阻塞,直到建立连接。 29 | Socket socket = serverSocket.accept(); 30 | System.out.printf("[%s] - 有一个客户端连上来了 - %s\n", Thread.currentThread().getName(), socket.getRemoteSocketAddress()); 31 | 32 | // 为连接创建一个独立的线程,进行接收数据 33 | new Thread(() -> socketHandler(socket)).start(); 34 | } 35 | } 36 | 37 | /** 38 | * 处理 socket 连接 39 | * 40 | * @param socket 41 | */ 42 | private static void socketHandler(Socket socket) { 43 | try { 44 | // 创建缓冲区数组,用于临时存储客户端发来的数据 45 | byte[] bytes = new byte[1024]; 46 | // 通过 socket 获取输入流 47 | InputStream inputStream = socket.getInputStream(); 48 | // 循环接消息,直到连接关闭 49 | while (true) { 50 | // 从输入流中读取数据,并将它们存储到缓冲区数组中。该方法会阻塞,直到输入数据可用、检查到文件结束或抛出异常 51 | int len = inputStream.read(bytes); 52 | if (len != -1) { 53 | String content = new String(bytes, 0, len); 54 | System.out.printf("[%s] - 接收客户端发来的内容:%s\n", Thread.currentThread().getName(), content); 55 | } else { 56 | System.out.printf("[%s] - 连接关闭...\n", Thread.currentThread().getName()); 57 | break; 58 | } 59 | } 60 | } catch (IOException e) { 61 | e.printStackTrace(); 62 | } finally { 63 | try { 64 | socket.close(); 65 | } catch (IOException e) { 66 | e.printStackTrace(); 67 | } 68 | } 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /design-patterns/src/main/java/io/github/gozhuyinglong/designpatterns/factory/FactorySimple.java: -------------------------------------------------------------------------------- 1 | package io.github.gozhuyinglong.designpatterns.factory; 2 | 3 | /** 4 | * 简单工厂模式 5 | * 6 | * @author 码农StayUp 7 | * @date 2020/12/8 0008 8 | */ 9 | public class FactorySimple { 10 | 11 | /** 12 | * 手机接口 13 | */ 14 | private interface Phone { 15 | 16 | /** 17 | * 打电话 18 | */ 19 | void call(); 20 | 21 | /** 22 | * 发短信 23 | */ 24 | void sendSMS(); 25 | } 26 | 27 | /** 28 | * 小米手机实现类 29 | */ 30 | private static class XiaomiPhone implements Phone { 31 | @Override 32 | public void call() { 33 | System.out.println("使用小米手机打电话"); 34 | } 35 | 36 | @Override 37 | public void sendSMS() { 38 | System.out.println("使用小米手机发短信"); 39 | } 40 | } 41 | 42 | /** 43 | * 红米手机实现类 44 | */ 45 | private static class RedmiPhone implements Phone { 46 | @Override 47 | public void call() { 48 | System.out.println("使用红米手机打电话"); 49 | } 50 | 51 | @Override 52 | public void sendSMS() { 53 | System.out.println("使用红米手机发短信"); 54 | } 55 | } 56 | 57 | /** 58 | * 手机工厂类 59 | */ 60 | private static class PhoneFactory { 61 | 62 | /** 63 | * 根据类型获取手机 64 | * 65 | * @param type 66 | * @return 67 | */ 68 | public static Phone createPhone(String type) { 69 | switch (type) { 70 | case "xiaomi": 71 | return new XiaomiPhone(); 72 | case "redmi": 73 | return new RedmiPhone(); 74 | default: 75 | return null; 76 | } 77 | } 78 | } 79 | 80 | public static void main(String[] args) { 81 | System.out.println("================小米手机================"); 82 | Phone xiaomiPhone = PhoneFactory.createPhone("xiaomi"); 83 | if (xiaomiPhone != null) { 84 | xiaomiPhone.call(); 85 | xiaomiPhone.sendSMS(); 86 | } 87 | 88 | System.out.println("================红米手机================"); 89 | Phone redmiPhone = PhoneFactory.createPhone("redmi"); 90 | if (redmiPhone != null) { 91 | redmiPhone.call(); 92 | redmiPhone.sendSMS(); 93 | } 94 | 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /design-patterns/src/main/java/io/github/gozhuyinglong/designpatterns/factory/FactoryMethod.java: -------------------------------------------------------------------------------- 1 | package io.github.gozhuyinglong.designpatterns.factory; 2 | 3 | /** 4 | * 工厂方法模式 5 | * 6 | * @author 码农StayUp 7 | * @date 2020/12/8 0008 8 | */ 9 | public class FactoryMethod { 10 | 11 | /** 12 | * 手机接口 13 | */ 14 | private interface Phone { 15 | 16 | /** 17 | * 打电话 18 | */ 19 | void call(); 20 | 21 | /** 22 | * 发短信 23 | */ 24 | void sendSMS(); 25 | } 26 | 27 | /** 28 | * 小米手机实现类 29 | */ 30 | private static class XiaomiPhone implements Phone { 31 | @Override 32 | public void call() { 33 | System.out.println("使用小米手机打电话"); 34 | } 35 | 36 | @Override 37 | public void sendSMS() { 38 | System.out.println("使用小米手机发短信"); 39 | } 40 | } 41 | 42 | /** 43 | * 红米手机实现类 44 | */ 45 | private static class RedmiPhone implements Phone { 46 | @Override 47 | public void call() { 48 | System.out.println("使用红米手机打电话"); 49 | } 50 | 51 | @Override 52 | public void sendSMS() { 53 | System.out.println("使用红米手机发短信"); 54 | } 55 | } 56 | 57 | /** 58 | * 手机工厂接口 59 | */ 60 | private interface PhoneFactory { 61 | /** 62 | * 获取手机实例 63 | * 64 | * @return 65 | */ 66 | Phone createPhone(); 67 | } 68 | 69 | /** 70 | * 小米手机工厂类 71 | */ 72 | private static class XiaomiPhoneFactory implements PhoneFactory { 73 | @Override 74 | public Phone createPhone() { 75 | return new XiaomiPhone(); 76 | } 77 | } 78 | 79 | /** 80 | * 红米手机工厂类 81 | */ 82 | private static class RedmiPhoneFactory implements PhoneFactory { 83 | @Override 84 | public Phone createPhone() { 85 | return new RedmiPhone(); 86 | } 87 | } 88 | 89 | public static void main(String[] args) { 90 | System.out.println("================小米手机================"); 91 | Phone xiaomiPhone = new XiaomiPhoneFactory().createPhone(); 92 | xiaomiPhone.call(); 93 | xiaomiPhone.sendSMS(); 94 | 95 | System.out.println("================红米手机================"); 96 | Phone redmiPhone = new RedmiPhoneFactory().createPhone(); 97 | redmiPhone.call(); 98 | redmiPhone.sendSMS(); 99 | } 100 | 101 | } 102 | -------------------------------------------------------------------------------- /springboot-family/redis-template-fastjson/src/main/java/io/github/gozhuyinglong/config/RedisConfig.java: -------------------------------------------------------------------------------- 1 | package io.github.gozhuyinglong.config; 2 | 3 | import com.alibaba.fastjson.JSON; 4 | import com.alibaba.fastjson.util.IOUtils; 5 | import org.springframework.context.annotation.Bean; 6 | import org.springframework.context.annotation.Configuration; 7 | import org.springframework.data.redis.connection.RedisConnectionFactory; 8 | import org.springframework.data.redis.core.RedisTemplate; 9 | import org.springframework.data.redis.serializer.RedisSerializer; 10 | import org.springframework.data.redis.serializer.SerializationException; 11 | import org.springframework.data.redis.serializer.StringRedisSerializer; 12 | 13 | /** 14 | * @author 码农StayUp 15 | * @date 2021/10/9 0010 16 | */ 17 | @Configuration 18 | public class RedisConfig { 19 | 20 | /** 21 | * redisTemplate 配置 22 | * 23 | * @return RedisTemplate 实例 24 | */ 25 | @Bean 26 | public RedisTemplate redisTemplate(RedisConnectionFactory redisConnectionFactory) { 27 | RedisTemplate redisTemplate = new RedisTemplate<>(); 28 | redisTemplate.setConnectionFactory(redisConnectionFactory); 29 | redisTemplate.setKeySerializer(new StringRedisSerializer()); 30 | redisTemplate.setValueSerializer(fastJsonRedisSerializer()); 31 | redisTemplate.setHashKeySerializer(new StringRedisSerializer()); 32 | redisTemplate.setHashValueSerializer(fastJsonRedisSerializer()); 33 | redisTemplate.afterPropertiesSet(); 34 | return redisTemplate; 35 | } 36 | 37 | /** 38 | * 自定义 redis 的序列化器:使用FastJson 39 | * 40 | * @return redis 序列化器 41 | */ 42 | @Bean 43 | public RedisSerializer fastJsonRedisSerializer() { 44 | return new RedisSerializer() { 45 | 46 | // 序列化 47 | @Override 48 | public byte[] serialize(Object o) throws SerializationException { 49 | if (o == null) { 50 | return new byte[0]; 51 | } 52 | if (o instanceof String) { 53 | return ((String) o).getBytes(IOUtils.UTF8); 54 | } 55 | return JSON.toJSONString(o).getBytes(IOUtils.UTF8); 56 | } 57 | 58 | // 反序列化 59 | @Override 60 | public Object deserialize(byte[] bytes) throws SerializationException { 61 | if (bytes == null || bytes.length == 0) { 62 | return null; 63 | } 64 | return new String(bytes, IOUtils.UTF8); 65 | } 66 | }; 67 | } 68 | 69 | } 70 | -------------------------------------------------------------------------------- /python-spider/area-number/area_number_2020.py: -------------------------------------------------------------------------------- 1 | # -*-coding:utf-8-*- 2 | import requests 3 | from bs4 import BeautifulSoup 4 | 5 | 6 | # 根据地址获取页面内容,并返回BeautifulSoup 7 | def get_html(url): 8 | # 若页面打开失败,则无限重试,没有后退可言 9 | while True: 10 | try: 11 | # 超时时间为1秒 12 | response = requests.get(url, timeout=1) 13 | response.encoding = "GBK" 14 | if response.status_code == 200: 15 | return BeautifulSoup(response.text, "lxml") 16 | else: 17 | continue 18 | except Exception: 19 | continue 20 | 21 | 22 | # 获取地址前缀(用于相对地址) 23 | def get_prefix(url): 24 | return url[0:url.rindex("/") + 1] 25 | 26 | 27 | # 递归抓取下一页面 28 | def spider_next(url, lev, parent_item_code): 29 | if lev == 2: 30 | spider_class = "city" 31 | elif lev == 3: 32 | spider_class = "county" 33 | elif lev == 4: 34 | spider_class = "town" 35 | else: 36 | spider_class = "village" 37 | 38 | item_list = get_html(url).select("tr." + spider_class + "tr") 39 | if len(item_list) > 0: 40 | for item in item_list: 41 | item_td = item.select("td") 42 | item_td_code = item_td[0].select_one("a") 43 | item_td_name = item_td[1].select_one("a") 44 | if item_td_code is None: 45 | item_href = None 46 | item_code = item_td[0].text 47 | item_name = item_td[1].text 48 | if lev == 5: 49 | item_name = item_td[2].text 50 | else: 51 | item_href = item_td_code.get("href") 52 | item_code = item_td_code.text 53 | item_name = item_td_name.text 54 | # 输出:级别、区划代码、名称 55 | content2 = str(lev) + "\t" + item_code + "\t" + item_name + "\t" + parent_item_code 56 | print(content2) 57 | f.write(content2 + "\n") 58 | if item_href is not None: 59 | spider_next(get_prefix(url) + item_href, lev + 1, item_code) 60 | else: 61 | spider_next(url, lev + 1, parent_item_code) 62 | 63 | 64 | 65 | # 入口 66 | if __name__ == '__main__': 67 | 68 | # 抓取省份页面 69 | province_url = "http://www.stats.gov.cn/tjsj/tjbz/tjyqhdmhcxhfdm/2020/index.html" 70 | province_list = get_html(province_url).select('tr.provincetr a') 71 | 72 | # 数据写入到当前文件夹下 area-number-2020.txt 中 73 | f = open("area-number-2020.txt", "w", encoding="utf-8") 74 | try: 75 | for province in province_list: 76 | href = province.get("href") 77 | province_code = href[0: 2] + "0000000000" 78 | province_name = province.text 79 | # 输出:级别、区划代码、名称 80 | content = "1\t" + province_code + "\t" + province_name 81 | print(content) 82 | f.write(content + "\n") 83 | spider_next(get_prefix(province_url) + href, 2, province_code) 84 | finally: 85 | f.close() 86 | -------------------------------------------------------------------------------- /java-data-structures/src/main/java/io/github/gozhuyinglong/datastructures/stack/StackDemo.java: -------------------------------------------------------------------------------- 1 | package io.github.gozhuyinglong.datastructures.stack; 2 | 3 | /** 4 | * 栈 5 | * 6 | * @author 码农StayUp 7 | * @date 2020/11/25 0025 8 | */ 9 | public class StackDemo { 10 | 11 | public static void main(String[] args) { 12 | 13 | System.out.println("-------------------入站"); 14 | Stack stack = new Stack<>(10); 15 | stack.push("a"); 16 | stack.push("b"); 17 | stack.push("c"); 18 | stack.push("d"); 19 | stack.push("e"); 20 | stack.print(); 21 | 22 | System.out.println("元素大小: " + stack.size()); 23 | System.out.println("栈容量: " + stack.capacity()); 24 | 25 | System.out.println("-------------------出站"); 26 | System.out.println("出站元素: " + stack.pop()); 27 | System.out.println("出站元素: " + stack.pop()); 28 | stack.print(); 29 | System.out.println("元素大小: " + stack.size()); 30 | System.out.println("栈容量: " + stack.capacity()); 31 | } 32 | 33 | private static class Stack { 34 | private int size; // 元素大小 35 | private final int capacity; // 栈的容量 36 | transient Object[] elementData; // 元素数据 37 | 38 | public Stack(int capacity) { 39 | if (capacity <= 0) { 40 | throw new IllegalArgumentException("Illegal Capacity: " + capacity); 41 | } else { 42 | this.capacity = capacity; 43 | elementData = new Object[capacity]; 44 | } 45 | } 46 | 47 | /** 48 | * 获取栈的元素大小 49 | * 50 | * @return 51 | */ 52 | public int size() { 53 | return size; 54 | } 55 | 56 | /** 57 | * 获取栈的容量 58 | * 59 | * @return 60 | */ 61 | public int capacity() { 62 | return capacity; 63 | } 64 | 65 | /** 66 | * 入栈 67 | * 68 | * @param e 69 | * @return 70 | */ 71 | public boolean push(E e) { 72 | if (size >= capacity) { 73 | return false; 74 | } 75 | elementData[size++] = e; 76 | return true; 77 | } 78 | 79 | /** 80 | * 出栈 81 | * 82 | * @return 83 | */ 84 | public E pop() { 85 | if (size <= 0) { 86 | return null; 87 | } 88 | return (E) elementData[--size]; 89 | } 90 | 91 | /** 92 | * 打印元素数据 93 | */ 94 | public void print() { 95 | System.out.print("站内元素: "); 96 | for (int i = 0; i < size; i++) { 97 | System.out.printf("%s\t", elementData[i]); 98 | } 99 | System.out.println(); 100 | } 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /netty-demo/src/main/java/io/github/gozhuyinglong/netty/server/NettyNioServer.java: -------------------------------------------------------------------------------- 1 | package io.github.gozhuyinglong.netty.server; 2 | 3 | import io.netty.bootstrap.ServerBootstrap; 4 | import io.netty.buffer.Unpooled; 5 | import io.netty.channel.ChannelFuture; 6 | import io.netty.channel.ChannelInitializer; 7 | import io.netty.channel.ChannelOption; 8 | import io.netty.channel.nio.NioEventLoopGroup; 9 | import io.netty.channel.socket.SocketChannel; 10 | import io.netty.channel.socket.nio.NioServerSocketChannel; 11 | import io.netty.handler.codec.DelimiterBasedFrameDecoder; 12 | import io.netty.handler.codec.FixedLengthFrameDecoder; 13 | import io.netty.handler.codec.LengthFieldBasedFrameDecoder; 14 | import io.netty.handler.codec.LineBasedFrameDecoder; 15 | 16 | /** 17 | * @author 码农StayUp 18 | * @date 2021/11/23 0023 19 | */ 20 | public class NettyNioServer { 21 | 22 | private final int port; 23 | 24 | public NettyNioServer(int port) { 25 | this.port = port; 26 | } 27 | 28 | public void run() throws Exception { 29 | // boss多线程事件循环组,用于接收连接 30 | NioEventLoopGroup bossGroup = new NioEventLoopGroup(); 31 | // worker多线程事件循环组,用于处理连接后的 I/O 数据 32 | NioEventLoopGroup workerGroup = new NioEventLoopGroup(); 33 | try { 34 | // 创建服务端引导程序,用于设置服务端配置 35 | ServerBootstrap sb = new ServerBootstrap(); 36 | sb.group(bossGroup, workerGroup) // 注册这两个线程组 37 | .channel(NioServerSocketChannel.class) // 设置接收新连接时使用的 I/O 模型,NioServerSocketChannel 类实现了NIO多路复用模型 38 | .childHandler(new ChannelInitializer() { // 设置 childHandler,用于处理具体的 Channel 的请求 39 | @Override 40 | protected void initChannel(SocketChannel ch) throws Exception { 41 | // ch.pipeline().addLast(new FixedLengthFrameDecoder(48)); 42 | // ch.pipeline().addLast(new LineBasedFrameDecoder(1024)); 43 | // ch.pipeline().addLast(new DelimiterBasedFrameDecoder(1024, Unpooled.copiedBuffer("$".getBytes()))); 44 | // ch.pipeline().addLast(new LengthFieldBasedFrameDecoder(1024, 1, 2, 0, 3)); 45 | ch.pipeline().addLast(new NettyNioServerHandler()); // 添加 I/O 事件的处理器 46 | } 47 | 48 | }) 49 | .option(ChannelOption.SO_BACKLOG, 128) // option用于设置套接字选项,SO_BACKLOG 用于设置连接队列大小 50 | .childOption(ChannelOption.SO_KEEPALIVE, true); 51 | // 绑定端口,并开始接收传入的连接 52 | ChannelFuture channelFuture = sb.bind(port).sync(); 53 | System.out.println("服务端启动成功,等待连接..."); 54 | // 阻塞住,直到套接字关闭。 55 | channelFuture.channel().closeFuture().sync(); 56 | 57 | } finally { 58 | // 优雅关闭两个线程组 59 | workerGroup.shutdownGracefully(); 60 | bossGroup.shutdownGracefully(); 61 | } 62 | } 63 | 64 | public static void main(String[] args) throws Exception { 65 | new NettyNioServer(8080).run(); 66 | } 67 | 68 | } 69 | -------------------------------------------------------------------------------- /java-data-structures/src/main/java/io/github/gozhuyinglong/datastructures/tree/TreeDemo.java: -------------------------------------------------------------------------------- 1 | package io.github.gozhuyinglong.datastructures.tree; 2 | 3 | /** 4 | * 树 5 | * 6 | * @author 码农StayUp 7 | * @date 2020/11/30 0030 8 | */ 9 | public class TreeDemo { 10 | 11 | public static void main(String[] args) { 12 | new Tree().initTree().listAll(); 13 | 14 | } 15 | 16 | private static class Tree { 17 | 18 | private TreeNode root; // 树根 19 | 20 | /** 21 | * 初始化一棵树 22 | */ 23 | private Tree initTree() { 24 | 25 | TreeNode a = new TreeNode("A"); 26 | TreeNode b = new TreeNode("B"); 27 | TreeNode c = new TreeNode("C"); 28 | TreeNode d = new TreeNode("D"); 29 | TreeNode e = new TreeNode("E"); 30 | TreeNode f = new TreeNode("F"); 31 | TreeNode g = new TreeNode("G"); 32 | TreeNode h = new TreeNode("H"); 33 | TreeNode i = new TreeNode("I"); 34 | TreeNode j = new TreeNode("J"); 35 | TreeNode k = new TreeNode("K"); 36 | TreeNode l = new TreeNode("L"); 37 | TreeNode m = new TreeNode("M"); 38 | 39 | root = a; 40 | 41 | a.firstChild = b; 42 | 43 | b.nextSibling = c; 44 | 45 | c.nextSibling = d; 46 | c.firstChild = f; 47 | 48 | d.nextSibling = e; 49 | d.firstChild = g; 50 | 51 | e.firstChild = i; 52 | 53 | g.nextSibling = h; 54 | 55 | h.firstChild = l; 56 | 57 | i.nextSibling = j; 58 | 59 | j.nextSibling = k; 60 | 61 | l.nextSibling = m; 62 | 63 | return this; 64 | } 65 | 66 | 67 | /** 68 | * 遍历一棵树,从root开始 69 | */ 70 | public void listAll() { 71 | listAll(root, 0); 72 | } 73 | 74 | /** 75 | * 遍历一棵树 76 | * 77 | * @param node 树节点 78 | * @param depth 层级(用于辅助输出) 79 | */ 80 | public void listAll(TreeNode node, int depth) { 81 | StringBuilder t = new StringBuilder(); 82 | for (int i = 0; i < depth; i++) { 83 | t.append("\t"); 84 | } 85 | System.out.printf("%s%s\n", t.toString(), node.element); 86 | 87 | // 先遍历子节点,子节点的层级需要+1 88 | if (node.firstChild != null) { 89 | listAll(node.firstChild, depth + 1); 90 | } 91 | 92 | // 后遍历兄弟节点,兄弟节点的层级不变 93 | if (node.nextSibling != null) { 94 | listAll(node.nextSibling, depth); 95 | } 96 | } 97 | 98 | 99 | } 100 | 101 | private static class TreeNode { 102 | private final Object element; // 当前节点数据 103 | private TreeNode firstChild; // 当前节点的第一个子节点 104 | private TreeNode nextSibling; // 当前节点的下一个兄弟节点 105 | 106 | public TreeNode(Object element) { 107 | this.element = element; 108 | } 109 | 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /netty-demo/src/main/java/io/github/gozhuyinglong/nio/NioServer.java: -------------------------------------------------------------------------------- 1 | package io.github.gozhuyinglong.nio; 2 | 3 | import java.io.IOException; 4 | import java.net.InetSocketAddress; 5 | import java.nio.ByteBuffer; 6 | import java.nio.channels.SelectionKey; 7 | import java.nio.channels.Selector; 8 | import java.nio.channels.ServerSocketChannel; 9 | import java.nio.channels.SocketChannel; 10 | import java.util.Iterator; 11 | 12 | /** 13 | * @author 码农StayUp 14 | * @date 2021/4/16 0016 15 | */ 16 | public class NioServer { 17 | 18 | private static final int PORT = 8080; 19 | 20 | public static void main(String[] args) throws IOException { 21 | 22 | // 在本地打开一个 ServerSocket 通道 23 | ServerSocketChannel serverSocketChannel = ServerSocketChannel.open(); 24 | // 将该通道配置为非阻塞 25 | serverSocketChannel.configureBlocking(false); 26 | // 绑定本地端口 27 | serverSocketChannel.bind(new InetSocketAddress(PORT)); 28 | System.out.printf("[%s] - 服务端启动了,端口为:%s\n", Thread.currentThread().getName(), PORT); 29 | 30 | // 打开一个选择器(多路复用) 31 | Selector selector = Selector.open(); 32 | // 将该通道的接入事件注册到选择器中 33 | serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT); 34 | 35 | // 通过选择器轮询获取该通道上的 I/O 事件。该操作会阻塞 36 | while (selector.select() > 0) { 37 | // 获取选择器中所有准备就绪的事件,并进行迭代 38 | Iterator it = selector.selectedKeys().iterator(); 39 | // 遍历所有准备好的事件 40 | while (it.hasNext()) { 41 | // 获取当前事件 42 | SelectionKey selectionKey = it.next(); 43 | // 从迭代器中移除当前事件 44 | it.remove(); 45 | // 根据不同的事件,做不同的操作 46 | if (selectionKey.isAcceptable()) { 47 | // 接收事件,表示一个 Socket 连接上来了 48 | // 获取当前接入的通道 49 | SocketChannel socketChannel = serverSocketChannel.accept(); 50 | System.out.printf("[%s] - 有一个客户端连上来了 - %s\n", Thread.currentThread().getName(), socketChannel.getRemoteAddress()); 51 | // 将接入的通道设为非阻塞 52 | socketChannel.configureBlocking(false); 53 | // 将该接入的通道的读事件注册到选择器中 54 | socketChannel.register(selector, SelectionKey.OP_READ); 55 | } else if (selectionKey.isReadable()) { 56 | // 读取事件,表示有客户端发来消息 57 | // 获取产生事件的通道 58 | socketHandler((SocketChannel) selectionKey.channel()); 59 | } 60 | } 61 | } 62 | } 63 | 64 | /** 65 | * 处理 Socket 通道 66 | * @param socketChannel 67 | * @throws IOException 68 | */ 69 | private static void socketHandler(SocketChannel socketChannel) throws IOException { 70 | // 申请一个1024个字节的缓冲区 71 | ByteBuffer byteBuffer = ByteBuffer.allocate(1024); 72 | // 读取该通道的内容至缓冲区(将内容写入缓冲区) 73 | while (socketChannel.read(byteBuffer) > 0) { 74 | // 将缓冲区进行反转(刚才是写入,反转后变为读取) 75 | byteBuffer.flip(); 76 | // 读取缓冲区中的内容,并转为字符串 77 | String content = new String(byteBuffer.array(), 0, byteBuffer.limit()); 78 | System.out.printf("[%s] - 接收客户端发来的内容:%s\n", Thread.currentThread().getName(), content); 79 | // 清除缓冲区 80 | byteBuffer.clear(); 81 | } 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /netty-demo/src/main/java/io/github/gozhuyinglong/netty/client/NettyNioClientHandler.java: -------------------------------------------------------------------------------- 1 | package io.github.gozhuyinglong.netty.client; 2 | 3 | import io.netty.buffer.ByteBuf; 4 | import io.netty.buffer.ByteBufUtil; 5 | import io.netty.buffer.Unpooled; 6 | import io.netty.channel.ChannelHandlerContext; 7 | import io.netty.channel.ChannelInboundHandlerAdapter; 8 | import io.netty.util.ReferenceCountUtil; 9 | 10 | import java.nio.charset.StandardCharsets; 11 | import java.util.concurrent.TimeUnit; 12 | 13 | /** 14 | * @author 码农StayUp 15 | * @date 2021/11/23 0023 16 | */ 17 | public class NettyNioClientHandler extends ChannelInboundHandlerAdapter { 18 | 19 | @Override 20 | public void channelActive(ChannelHandlerContext ctx) throws Exception { 21 | System.out.println("连接成功..."); 22 | 23 | // 正常 24 | for (int i = 0; i < 10; i++) { 25 | ByteBuf byteBuf = Unpooled.copiedBuffer("服务器你好,我是一条完整的消息!".getBytes(StandardCharsets.UTF_8)); 26 | ctx.writeAndFlush(byteBuf); 27 | TimeUnit.SECONDS.sleep(1); 28 | } 29 | 30 | // 发生粘包/拆包 31 | // for (int i = 0; i < 100; i++) { 32 | // ByteBuf byteBuf = Unpooled.copiedBuffer("服务器你好,我是一条完整的消息!".getBytes(StandardCharsets.UTF_8)); 33 | // ctx.writeAndFlush(byteBuf); 34 | // } 35 | 36 | // 1.测试 FixedLengthFrameDecoder 37 | // for (int i = 0; i < 10; i++) { 38 | // ByteBuf byteBuf = Unpooled.copiedBuffer("服务器你好,我是一条完整的消息!".getBytes(StandardCharsets.UTF_8)); 39 | // ctx.writeAndFlush(byteBuf); 40 | // } 41 | 42 | // 2.测试 LineBasedFrameDecoder 43 | // for (int i = 0; i < 10; i++) { 44 | // ByteBuf byteBuf = Unpooled.copiedBuffer("服务器你好,我是一条完整的消息!".getBytes(StandardCharsets.UTF_8)); 45 | // ctx.writeAndFlush(byteBuf); 46 | // } 47 | 48 | // 3.测试 DelimiterBasedFrameDecoder 49 | // for (int i = 0; i < 10; i++) { 50 | // ByteBuf byteBuf = Unpooled.copiedBuffer("服务器你好,我是一条完整的消息!$".getBytes(StandardCharsets.UTF_8)); 51 | // ctx.writeAndFlush(byteBuf); 52 | // } 53 | 54 | // 4.测试 LengthFieldBasedFrameDecoder 55 | // for (int i = 0; i < 10; i++) { 56 | // byte[] header = ByteBufUtil.decodeHexDump("EE"); 57 | // byte[] length = ByteBufUtil.decodeHexDump("0030"); 58 | // byte[] data = "服务器你好,我是一条完整的消息!".getBytes(StandardCharsets.UTF_8); 59 | // ByteBuf byteBuf = Unpooled.copiedBuffer(header, length, data); 60 | // ctx.writeAndFlush(byteBuf); 61 | // } 62 | } 63 | 64 | @Override 65 | public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { 66 | // 接收到的消息 67 | ByteBuf in = (ByteBuf) msg; 68 | try { 69 | byte[] b = new byte[in.readableBytes()]; 70 | in.readBytes(b); 71 | System.out.println(new String(b, StandardCharsets.UTF_8)); 72 | } finally { 73 | // 显式的释放消息 74 | ReferenceCountUtil.release(msg); 75 | } 76 | } 77 | 78 | @Override 79 | public void channelReadComplete(ChannelHandlerContext ctx) throws Exception { 80 | ctx.flush(); 81 | } 82 | 83 | @Override 84 | public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { 85 | ctx.close(); 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /java-data-structures/src/main/java/io/github/gozhuyinglong/datastructures/linkedlist/CircularLinkedListDemo.java: -------------------------------------------------------------------------------- 1 | package io.github.gozhuyinglong.datastructures.linkedlist; 2 | 3 | /** 4 | * 环形链表 5 | * 6 | * @author 码农StayUp 7 | * @date 2020/11/22 0022 8 | */ 9 | public class CircularLinkedListDemo { 10 | 11 | public static void main(String[] args) { 12 | 13 | CircularLinkedList circularLinkedList = new CircularLinkedList(); 14 | 15 | System.out.println("-----------添加10个节点"); 16 | for (int i = 1; i <= 10; i++) { 17 | circularLinkedList.add(new Node(i)); 18 | } 19 | circularLinkedList.print(); 20 | 21 | System.out.println("-----------按约瑟夫问题顺序出列"); 22 | circularLinkedList.josephusProblem(3); 23 | 24 | } 25 | 26 | private static class CircularLinkedList { 27 | private Node first = null; // 头部节点,即第一个节点 28 | 29 | /** 30 | * 添加节点,并将新添加的节点的next指向头部,形成一个环形 31 | * 32 | * @param node 33 | * @return 34 | */ 35 | public void add(Node node) { 36 | if (first == null) { 37 | first = node; 38 | first.next = first; 39 | return; 40 | } 41 | 42 | Node temp = first; 43 | while (true) { 44 | if (temp.next == null || temp.next == first) { 45 | temp.next = node; 46 | node.next = first; 47 | break; 48 | } 49 | temp = temp.next; 50 | } 51 | } 52 | 53 | /** 54 | * 按约瑟夫问题顺序出列 55 | * 即从第1个元素开始报数,报到num时当前元素出列,然后重新从下一个元素开始报数,直至所有元素出列 56 | * 57 | * @param num 表示报几次数 58 | */ 59 | public void josephusProblem(int num) { 60 | Node currentNode = first; 61 | // 将当前节点指向最后一个节点 62 | do { 63 | currentNode = currentNode.next; 64 | } while (currentNode.next != first); 65 | 66 | // 开始出列 67 | while (true) { 68 | // 当前节点要指向待出列节点的前一节点(双向环形队列不需要) 69 | for (int i = 0; i < num - 1; i++) { 70 | currentNode = currentNode.next; 71 | } 72 | System.out.printf("%s\t", currentNode.next.no); 73 | if(currentNode.next == currentNode){ 74 | break; 75 | } 76 | currentNode.next = currentNode.next.next; 77 | } 78 | } 79 | 80 | /** 81 | * 输出节点 82 | */ 83 | public void print() { 84 | if (first == null) { 85 | return; 86 | } 87 | 88 | Node temp = first; 89 | while (true) { 90 | System.out.printf("%s\t", temp.no); 91 | if (temp.next == first) { 92 | break; 93 | } 94 | temp = temp.next; 95 | } 96 | System.out.println(); 97 | } 98 | } 99 | 100 | private static class Node { 101 | private final int no; 102 | private Node next; // 指向下一节点 103 | 104 | public Node(int no) { 105 | this.no = no; 106 | } 107 | 108 | @Override 109 | public String toString() { 110 | return "Node{" + 111 | "no=" + no + 112 | '}'; 113 | } 114 | } 115 | } 116 | -------------------------------------------------------------------------------- /java-data-structures/src/main/java/io/github/gozhuyinglong/datastructures/array/SparseArrayDemo.java: -------------------------------------------------------------------------------- 1 | package io.github.gozhuyinglong.datastructures.array; 2 | 3 | /** 4 | * 稀疏数组 5 | * 6 | * @author 码农StayUp 7 | * @date 2020/11/16 8 | */ 9 | public class SparseArrayDemo { 10 | 11 | public static void main(String[] args) { 12 | System.out.println("-----------------------普通数组"); 13 | int[][] initialArray = initArray(); 14 | printArray(initialArray); 15 | System.out.println("-----------------------普通数组 --> 稀疏数组"); 16 | int[][] sparseArray = arrayConvertSparseArray(initialArray); 17 | printArray(sparseArray); 18 | System.out.println("-----------------------稀疏数组 --> 普通数组"); 19 | int[][] array = sparseArrayConvertArray(sparseArray); 20 | printArray(array); 21 | } 22 | 23 | /** 24 | * 初始化五子棋数组 25 | * 26 | * @return 27 | */ 28 | static int[][] initArray() { 29 | // 0为空,1为白子,2为黑子 30 | int[][] array = new int[15][15]; 31 | array[3][11] = 1; 32 | array[4][10] = 2; 33 | array[5][9] = 2; 34 | array[6][8] = 2; 35 | array[6][7] = 1; 36 | array[7][8] = 1; 37 | array[7][7] = 2; 38 | array[8][6] = 1; 39 | return array; 40 | } 41 | 42 | /** 43 | * 打印二维数组 44 | * 45 | * @param array 46 | */ 47 | static void printArray(int[][] array) { 48 | for (int[] row : array) { 49 | for (int data : row) { 50 | System.out.printf("%s\t", data); 51 | } 52 | System.out.println(); 53 | } 54 | } 55 | 56 | /** 57 | * 统计非零值数量 58 | * 59 | * @param array 60 | * @return 61 | */ 62 | static int valueCount(int[][] array) { 63 | int count = 0; 64 | for (int[] row : array) { 65 | for (int data : row) { 66 | if (data != 0) { 67 | count++; 68 | } 69 | } 70 | } 71 | return count; 72 | } 73 | 74 | /** 75 | * 普通数组转为稀疏数组 76 | * 77 | * @param array 78 | * @return 79 | */ 80 | static int[][] arrayConvertSparseArray(int[][] array) { 81 | int rowNum = array.length; 82 | int colNum = array[0].length; 83 | int valueNum = valueCount(array); 84 | 85 | int[][] sparseArray = new int[valueNum + 1][3]; 86 | sparseArray[0][0] = rowNum; 87 | sparseArray[0][1] = colNum; 88 | sparseArray[0][2] = valueNum; 89 | 90 | int rowCount = 1; 91 | for (int i = 0; i < array.length; i++) { 92 | for (int j = 0; j < array[i].length; j++) { 93 | int value = array[i][j]; 94 | if (value != 0) { 95 | sparseArray[rowCount][0] = i; 96 | sparseArray[rowCount][1] = j; 97 | sparseArray[rowCount][2] = value; 98 | rowCount++; 99 | } 100 | } 101 | } 102 | return sparseArray; 103 | } 104 | 105 | /** 106 | * 稀疏数组转为普通数组 107 | * 108 | * @param sparseArray 109 | * @return 110 | */ 111 | static int[][] sparseArrayConvertArray(int[][] sparseArray) { 112 | int rowNum = sparseArray[0][0]; 113 | int colNum = sparseArray[0][1]; 114 | int valueNum = sparseArray[0][2]; 115 | 116 | int[][] array = new int[rowNum][colNum]; 117 | 118 | for (int i = 1; i < valueNum + 1; i++) { 119 | int row = sparseArray[i][0]; 120 | int col = sparseArray[i][1]; 121 | int value = sparseArray[i][2]; 122 | array[row][col] = value; 123 | } 124 | 125 | return array; 126 | } 127 | } 128 | -------------------------------------------------------------------------------- /java-source-analysis/src/main/java/io/github/gozhuyinglong/utils/CodeUtil.java: -------------------------------------------------------------------------------- 1 | package io.github.gozhuyinglong.utils; 2 | 3 | import java.io.UnsupportedEncodingException; 4 | import java.net.URLDecoder; 5 | import java.net.URLEncoder; 6 | import java.nio.charset.Charset; 7 | import java.util.Base64; 8 | 9 | /** 10 | * 编解码工具箱 11 | * 12 | * @author 码农StayUp 13 | * @date 2021/10/8 0008 14 | */ 15 | public class CodeUtil { 16 | 17 | /** 18 | * Base64 编码 19 | * 20 | * @param data 要编码的数据 21 | * @return 22 | */ 23 | public static String encodeBase64(String data) { 24 | return encodeBase64(data, Charset.defaultCharset()); 25 | } 26 | 27 | /** 28 | * Base64 编码 29 | * 30 | * @param data 要编码的数据 31 | * @param charset 字符集 32 | * @return 33 | */ 34 | public static String encodeBase64(String data, Charset charset) { 35 | byte[] binaryData = Base64.getEncoder().encode(data.getBytes(charset)); 36 | return new String(binaryData); 37 | } 38 | 39 | /** 40 | * Base64 解码 41 | * 42 | * @param data 要解码的数据 43 | * @return 44 | */ 45 | public static String decodeBase64(String data) { 46 | return decodeBase64(data, Charset.defaultCharset()); 47 | } 48 | 49 | /** 50 | * Base64 解码 51 | * 52 | * @param data 要解码的数据 53 | * @param charset 字符集 54 | * @return 55 | */ 56 | public static String decodeBase64(String data, Charset charset) { 57 | byte[] binaryData = Base64.getDecoder().decode(data.getBytes()); 58 | return new String(binaryData, charset); 59 | } 60 | 61 | /** 62 | * URL 编码 63 | * 64 | * @param data 要编码的数据 65 | * @return 66 | */ 67 | public static String encodeURL(String data) { 68 | return encodeURL(data, Charset.defaultCharset()); 69 | } 70 | 71 | /** 72 | * URL 编码 73 | * 74 | * @param data 要编码的数据 75 | * @param charset 字符集 76 | * @return 77 | */ 78 | public static String encodeURL(String data, Charset charset) { 79 | try { 80 | return URLEncoder.encode(data, charset.name()); 81 | } catch (UnsupportedEncodingException e) { 82 | e.printStackTrace(); 83 | } 84 | return null; 85 | } 86 | 87 | /** 88 | * URL 解码 89 | * 90 | * @param data 要解码的数据 91 | * @return 92 | */ 93 | public static String decodeURL(String data) { 94 | return decodeURL(data, Charset.defaultCharset()); 95 | } 96 | 97 | /** 98 | * URL 解码 99 | * 100 | * @param data 要解码的数据 101 | * @param charset 字符集 102 | * @return 103 | */ 104 | public static String decodeURL(String data, Charset charset) { 105 | try { 106 | return URLDecoder.decode(data, charset.name()); 107 | } catch (UnsupportedEncodingException e) { 108 | e.printStackTrace(); 109 | } 110 | return null; 111 | } 112 | 113 | public static void main(String[] args) { 114 | System.out.println(encodeBase64("码农StayUp")); 115 | System.out.println(decodeBase64("56CB5YacU3RheVVw")); 116 | 117 | System.out.println(encodeBase64("码农StayUp", Charset.forName("GBK"))); 118 | System.out.println(decodeBase64("wuvFqVN0YXlVcA==", Charset.forName("GBK"))); 119 | 120 | System.out.println(encodeURL("http://www.baidu.com?name=码农StayUp")); 121 | System.out.println(decodeURL("http%3A%2F%2Fwww.baidu.com%3Fname%3D%E7%A0%81%E5%86%9CStayUp")); 122 | 123 | System.out.println(encodeURL("http://www.baidu.com?name=码农StayUp", Charset.forName("GBK"))); 124 | System.out.println(decodeURL("http%3A%2F%2Fwww.baidu.com%3Fname%3D%C2%EB%C5%A9StayUp", Charset.forName("GBK"))); 125 | } 126 | } 127 | -------------------------------------------------------------------------------- /wechat-develop/src/main/java/io/github/gozhuyinglong/wechatdevelop/controller/WechatController.java: -------------------------------------------------------------------------------- 1 | package io.github.gozhuyinglong.wechatdevelop.controller; 2 | 3 | import com.alibaba.fastjson.JSONObject; 4 | import io.github.gozhuyinglong.wechatdevelop.dto.TokenDTO; 5 | import io.github.gozhuyinglong.wechatdevelop.utils.JwtUtil; 6 | import org.springframework.beans.factory.annotation.Autowired; 7 | import org.springframework.http.ResponseEntity; 8 | import org.springframework.web.bind.annotation.GetMapping; 9 | import org.springframework.web.bind.annotation.RequestMapping; 10 | import org.springframework.web.bind.annotation.RestController; 11 | import org.springframework.web.client.RestTemplate; 12 | 13 | import javax.servlet.http.HttpServletResponse; 14 | import java.io.IOException; 15 | import java.net.URLEncoder; 16 | 17 | @RestController 18 | @RequestMapping("/wechat") 19 | public class WechatController { 20 | 21 | private static final String APP_ID = "wxce34f24b4736434a"; 22 | private static final String APP_SECRET = "3ab7b64fc2d8c84713e7414d8a628306"; 23 | private static final String REDIRECT_URI = "http://127.0.0.1:8080/wechat/accessToken"; 24 | 25 | @Autowired 26 | private RestTemplate restTemplate; 27 | 28 | @GetMapping("/authorize") 29 | public void authorize(HttpServletResponse response) throws IOException { 30 | 31 | String url = "https://open.weixin.qq.com/connect/oauth2/authorize?" + 32 | "appid=" + APP_ID + 33 | "&redirect_uri=" + URLEncoder.encode(REDIRECT_URI, "UTF-8") + 34 | "&response_type=code" + 35 | "&scope=snsapi_userinfo" + 36 | "&state=STATE" + 37 | "#wechat_redirec"; 38 | response.sendRedirect(url); 39 | } 40 | 41 | @GetMapping("/accessToken") 42 | public TokenDTO accessToken(String code) { 43 | String url = "https://api.weixin.qq.com/sns/oauth2/access_token?" + 44 | "appid=" + APP_ID + 45 | "&secret=" + APP_SECRET + 46 | "&code=" + code + 47 | "&grant_type=authorization_code"; 48 | ResponseEntity responseEntity = restTemplate.getForEntity(url, String.class); 49 | String body = responseEntity.getBody(); 50 | JSONObject json = JSONObject.parseObject(body); 51 | if (json == null) { 52 | return null; 53 | } 54 | System.out.println("-------------------获取openid-------------------"); 55 | System.out.println(json.toJSONString()); 56 | String accessToken = json.getString("access_token"); 57 | int expiresIn = json.getIntValue("expires_in"); 58 | String openid = json.getString("openid"); 59 | String refreshToken = json.getString("refresh_token"); 60 | String scope = json.getString("scope"); 61 | 62 | // 获取微信用户信息 63 | userinfo(accessToken, openid); 64 | 65 | // 封装 TokenDTO 类 66 | TokenDTO dto = new TokenDTO(); 67 | dto.setToken(JwtUtil.generateToken(openid)); 68 | dto.setTokenHeader(JwtUtil.TOKEN_HEADER); 69 | dto.setTokenPrefix(JwtUtil.TOKEN_PREFIX); 70 | dto.setTtl(JwtUtil.TTL); 71 | return dto; 72 | } 73 | 74 | private void userinfo(String accessToken, String openid) { 75 | String url = "https://api.weixin.qq.com/sns/userinfo?" + 76 | "access_token=" + accessToken + 77 | "&openid=" + openid + 78 | "&lang=zh_CN"; 79 | ResponseEntity responseEntity = restTemplate.getForEntity(url, String.class); 80 | String body = responseEntity.getBody(); 81 | JSONObject json = JSONObject.parseObject(body); 82 | if (json == null) { 83 | return; 84 | } 85 | System.out.println("-------------------获取用户信息-------------------"); 86 | System.out.println(json.toJSONString()); 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /java-data-structures/src/main/java/io/github/gozhuyinglong/datastructures/stack/StackDemoBalancedChar.java: -------------------------------------------------------------------------------- 1 | package io.github.gozhuyinglong.datastructures.stack; 2 | 3 | import java.util.Stack; 4 | 5 | /** 6 | * 栈的应用 - 平衡符 7 | * 8 | * @author 码农StayUp 9 | * @date 2020/11/25 0025 10 | */ 11 | public class StackDemoBalancedChar { 12 | 13 | public static void main(String[] args) { 14 | 15 | BalancedChar balancedChar = new BalancedChar(); 16 | String str = "[()][{}][][((()))]"; 17 | boolean ok = balancedChar.isOk(str); 18 | System.out.printf("字符串:%s\t----> %s", str, ok); 19 | } 20 | 21 | private static class BalancedChar { 22 | private final char[] openArray = {'(', '[', '{'}; // 左括号 23 | private final char[] closeArray = {')', ']', '}'}; // 右括号 24 | 25 | /** 26 | * 判断字符串是否正确 27 | * 28 | * @param str 29 | * @return 30 | */ 31 | public boolean isOk(String str) { 32 | // 使用 Java 自带的 Stack 类 33 | Stack stack = new Stack<>(); 34 | 35 | boolean ok = true; // 判断字符串是否正确 36 | 37 | for (char c : str.toCharArray()) { 38 | 39 | // 若不是平衡符则忽略 40 | if (!isBalancedChar(c)) { 41 | continue; 42 | } 43 | 44 | // 如果是左括号,则入栈 45 | if (isOpen(c)) { 46 | stack.push(c); 47 | continue; 48 | } 49 | 50 | // 如果是右括号,而栈为空则报错 51 | if (stack.empty()) { 52 | ok = false; 53 | break; 54 | } 55 | // 如果是右括号,从栈中取出一个元素,并与当前元素判断是否是一对,若不是一对则报错 56 | Character open = stack.pop(); 57 | if (!isTwain(open, c)) { 58 | ok = false; 59 | } 60 | } 61 | 62 | return ok && stack.empty(); 63 | } 64 | 65 | /** 66 | * 是否为左括号 67 | * 68 | * @param c 69 | * @return 70 | */ 71 | public boolean isOpen(char c) { 72 | return inArray(openArray, c); 73 | } 74 | 75 | /** 76 | * 是否为右括号 77 | * 78 | * @param c 79 | * @return 80 | */ 81 | public boolean isClose(char c) { 82 | return inArray(closeArray, c); 83 | } 84 | 85 | /** 86 | * 是否是平衡符 87 | */ 88 | public boolean isBalancedChar(char c) { 89 | return isOpen(c) || isClose(c); 90 | } 91 | 92 | /** 93 | * 是否在数组中 94 | * 95 | * @param charArray 96 | * @param c 97 | * @return 98 | */ 99 | public boolean inArray(char[] charArray, char c) { 100 | for (char c1 : charArray) { 101 | if (c1 == c) { 102 | return true; 103 | } 104 | } 105 | return false; 106 | } 107 | 108 | /** 109 | * 是否一对平衡符 110 | * 111 | * @param open 112 | * @param close 113 | * @return 114 | */ 115 | public boolean isTwain(char open, char close) { 116 | switch (open) { 117 | case '(': 118 | if (close == ')') { 119 | return true; 120 | } 121 | case '[': 122 | if (close == ']') { 123 | return true; 124 | } 125 | case '{': 126 | if (close == '}') { 127 | return true; 128 | } 129 | default: 130 | return false; 131 | } 132 | } 133 | 134 | } 135 | } 136 | -------------------------------------------------------------------------------- /java-data-structures/src/main/java/io/github/gozhuyinglong/datastructures/queue/ArrayQueueDemo.java: -------------------------------------------------------------------------------- 1 | package io.github.gozhuyinglong.datastructures.queue; 2 | 3 | /** 4 | * 队列 - 数组实现 5 | * 6 | * @author 码农StayUp 7 | * @date 2020/11/26 0026 8 | */ 9 | public class ArrayQueueDemo { 10 | 11 | public static void main(String[] args) { 12 | ArrayQueue queue = new ArrayQueue<>(5); 13 | System.out.printf("头指针: %s\t尾指针: %s\t队列大小: %s\t容量: %s\n", queue.head, queue.tail, queue.size(), queue.capacity); 14 | System.out.println("出队: --> " + queue.get()); 15 | System.out.println("入队:1 --> " + queue.add(1)); 16 | System.out.println("入队:2 --> " + queue.add(2)); 17 | System.out.println("入队:3 --> " + queue.add(3)); 18 | System.out.println("入队:4 --> " + queue.add(4)); 19 | System.out.println("入队:5 --> " + queue.add(5)); 20 | 21 | System.out.printf("头指针: %s\t尾指针: %s\t队列大小: %s\t容量: %s\n", queue.head, queue.tail, queue.size(), queue.capacity); 22 | System.out.println("出队: --> " + queue.get()); 23 | System.out.println("入队:6 --> " + queue.add(6)); 24 | System.out.printf("头指针: %s\t尾指针: %s\t队列大小: %s\t容量: %s\n", queue.head, queue.tail, queue.size(), queue.capacity); 25 | System.out.println("入队:7 --> " + queue.add(7)); 26 | System.out.println("出队: --> " + queue.get()); 27 | System.out.println("出队: --> " + queue.get()); 28 | System.out.printf("头指针: %s\t尾指针: %s\t队列大小: %s\t容量: %s\n", queue.head, queue.tail, queue.size(), queue.capacity); 29 | System.out.println("入队:8 --> " + queue.add(8)); 30 | System.out.println("入队:9 --> " + queue.add(9)); 31 | System.out.printf("头指针: %s\t尾指针: %s\t队列大小: %s\t容量: %s\n", queue.head, queue.tail, queue.size(), queue.capacity); 32 | System.out.println("出队: --> " + queue.get()); 33 | System.out.println("出队: --> " + queue.get()); 34 | System.out.println("出队: --> " + queue.get()); 35 | System.out.println("出队: --> " + queue.get()); 36 | System.out.println("出队: --> " + queue.get()); 37 | System.out.printf("头指针: %s\t尾指针: %s\t队列大小: %s\t容量: %s\n", queue.head, queue.tail, queue.size(), queue.capacity); 38 | System.out.println("入队:10 --> " + queue.add(10)); 39 | System.out.printf("头指针: %s\t尾指针: %s\t队列大小: %s\t容量: %s\n", queue.head, queue.tail, queue.size(), queue.capacity); 40 | } 41 | 42 | 43 | private static class ArrayQueue { 44 | 45 | private final T[] queue; // 存储队列数据元素 46 | private final int capacity; // 容量 47 | private int head = 0; // 头部指针,指向队头元素 48 | private int tail = 0; // 尾部指针,指向下一个待入队元素的存储位置 49 | 50 | public ArrayQueue(int capacity) { 51 | this.capacity = capacity + 1; // 环形队列需要空出一个位置,来满足队列满时head与tail不重合 52 | this.queue = (T[]) new Object[this.capacity]; 53 | } 54 | 55 | /** 56 | * 向队列添加一个元素 57 | * 58 | * @param data 59 | * @return 60 | */ 61 | public boolean add(T data) { 62 | // 队列满,添加失败 63 | if (isFull()) { 64 | return false; 65 | } 66 | // tail指向下一个待入队元素的存储位置,所以先赋值再让指针加1 67 | queue[tail] = data; 68 | // 环形数组需要取模运算 69 | tail = (tail + 1) % capacity; 70 | return true; 71 | } 72 | 73 | /** 74 | * 从队列中获取一个元素 75 | * 76 | * @return 77 | */ 78 | public T get() { 79 | if (isEmpty()) { 80 | return null; 81 | } 82 | // head指向头元素位置,所以先取出再让指针加1 83 | T data = queue[head]; 84 | // 环形数组需要取模运算 85 | head = (head + 1) % capacity; 86 | return data; 87 | } 88 | 89 | /** 90 | * 当前队列大小 91 | * 92 | * @return 93 | */ 94 | public int size() { 95 | int size = tail - head; 96 | if (size < 0) { 97 | size += capacity; 98 | } 99 | return size; 100 | } 101 | 102 | /** 103 | * 队列是否为空:当tail与head指向同一位置时,表示队列为空 104 | * 105 | * @return 106 | */ 107 | public boolean isEmpty() { 108 | return tail == head; 109 | } 110 | 111 | /** 112 | * 队列是否已满:因为预留了一个位置,所以tail需要加1;环形队列需要取模运算 113 | * 114 | * @return 115 | */ 116 | public boolean isFull() { 117 | return head == (tail + 1) % capacity; 118 | } 119 | 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /netty-demo/src/main/java/io/github/gozhuyinglong/aio/AioServer.java: -------------------------------------------------------------------------------- 1 | package io.github.gozhuyinglong.aio; 2 | 3 | import java.io.IOException; 4 | import java.net.InetSocketAddress; 5 | import java.nio.ByteBuffer; 6 | import java.nio.channels.AsynchronousServerSocketChannel; 7 | import java.nio.channels.AsynchronousSocketChannel; 8 | import java.nio.channels.CompletionHandler; 9 | import java.util.concurrent.TimeUnit; 10 | 11 | /** 12 | * @author 码农StayUp 13 | * @date 2021/4/21 0021 14 | */ 15 | public class AioServer { 16 | 17 | private static final int PORT = 8080; 18 | 19 | public AioServer(int port) { 20 | try { 21 | // 打开一个异步的 ServerSocket 通道 22 | AsynchronousServerSocketChannel serverSocketChannel = AsynchronousServerSocketChannel.open(); 23 | // 绑定本地端口 24 | serverSocketChannel.bind(new InetSocketAddress(port)); 25 | System.out.printf("[%s] - 服务端启动了,端口为:%s\n", Thread.currentThread().getName(), port); 26 | // 接收客户端连接 27 | serverSocketChannel.accept(serverSocketChannel, new CompletionHandler() { 28 | @Override 29 | public void completed(AsynchronousSocketChannel result, AsynchronousServerSocketChannel attachment) { 30 | try { 31 | // 一个客户端连接后,继续接收下一个连接 32 | attachment.accept(attachment, this); 33 | System.out.printf("[%s] - 有一个客户端连上来了 - %s\n", Thread.currentThread().getName(), result.getRemoteAddress()); 34 | // 申请一个1024个字节的缓冲区 35 | ByteBuffer byteBuffer = ByteBuffer.allocate(1024); 36 | // 读取客户端数据 37 | result.read(byteBuffer, byteBuffer, new ReadCompletionHandler(result)); 38 | } catch (IOException e) { 39 | e.printStackTrace(); 40 | } 41 | } 42 | 43 | @Override 44 | public void failed(Throwable exc, AsynchronousServerSocketChannel attachment) { 45 | // 一个客户端连失败,继续接收下一个连接 46 | attachment.accept(attachment, this); 47 | System.out.printf("[%s] - 客户连接失败 - %s\n", Thread.currentThread().getName(), exc.getMessage()); 48 | } 49 | }); 50 | 51 | } catch (Exception e) { 52 | e.printStackTrace(); 53 | } 54 | } 55 | 56 | /** 57 | * Socket 处理类 58 | */ 59 | static class ReadCompletionHandler implements CompletionHandler { 60 | 61 | private final AsynchronousSocketChannel asynchronousSocketChannel; 62 | 63 | ReadCompletionHandler(AsynchronousSocketChannel asynchronousSocketChannel) { 64 | this.asynchronousSocketChannel = asynchronousSocketChannel; 65 | } 66 | 67 | @Override 68 | public void completed(Integer result, ByteBuffer attachment) { 69 | // 客户端关闭通道,字节数为-1 70 | if(result == -1) { 71 | System.out.printf("[%s] - 客户端断开连接!\n", Thread.currentThread().getName()); 72 | try { 73 | // 关闭当前 Socket 通道 74 | asynchronousSocketChannel.close(); 75 | } catch (IOException e) { 76 | e.printStackTrace(); 77 | } 78 | return; 79 | } 80 | // 将缓冲区进行反转(刚才是写入,反转后变为读取) 81 | attachment.flip(); 82 | // 读取缓冲区中的内容,并转为字符串 83 | String content = new String(attachment.array(), 0, result); 84 | System.out.printf("[%s] - 接收客户端发来的内容:%s\n", Thread.currentThread().getName(), content); 85 | // 清除缓冲区 86 | attachment.clear(); 87 | // 继续读取下一报文 88 | asynchronousSocketChannel.read(attachment, attachment, new ReadCompletionHandler(asynchronousSocketChannel)); 89 | } 90 | 91 | @Override 92 | public void failed(Throwable exc, ByteBuffer attachment) { 93 | System.out.printf("[%s] - 客户端断开连接!\n", Thread.currentThread().getName()); 94 | try { 95 | // 关闭当前 Socket 通道 96 | asynchronousSocketChannel.close(); 97 | } catch (IOException e) { 98 | e.printStackTrace(); 99 | } 100 | } 101 | } 102 | 103 | 104 | public static void main(String[] args) throws Exception { 105 | 106 | AioServer server = new AioServer(PORT); 107 | TimeUnit.SECONDS.sleep(Integer.MAX_VALUE); 108 | 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /java-data-structures/src/main/java/io/github/gozhuyinglong/datastructures/tree/BinaryTreeDemo.java: -------------------------------------------------------------------------------- 1 | package io.github.gozhuyinglong.datastructures.tree; 2 | 3 | /** 4 | * 二叉树 5 | * 6 | * @author 码农StayUp 7 | * @date 2020/11/30 0030 8 | */ 9 | public class BinaryTreeDemo { 10 | 11 | public static void main(String[] args) { 12 | 13 | BinaryTree tree = new BinaryTree(); 14 | 15 | Node a = new Node("A"); 16 | Node b = new Node("B"); 17 | Node c = new Node("C"); 18 | Node d = new Node("D"); 19 | Node e = new Node("E"); 20 | Node f = new Node("F"); 21 | Node g = new Node("G"); 22 | Node h = new Node("H"); 23 | Node i = new Node("I"); 24 | 25 | tree.setRoot(a); 26 | a.left = b; 27 | a.right = c; 28 | b.left = d; 29 | b.right = e; 30 | d.left = h; 31 | c.left = f; 32 | c.right = g; 33 | g.right = i; 34 | 35 | System.out.println("---------------前序遍历"); 36 | tree.preOrder(); 37 | System.out.println("---------------中序遍历"); 38 | tree.inOrder(); 39 | System.out.println("---------------后序遍历"); 40 | tree.postOrder(); 41 | 42 | } 43 | 44 | private static class BinaryTree { 45 | 46 | private Node root; // 根 47 | 48 | public void setRoot(Node root) { 49 | this.root = root; 50 | } 51 | 52 | /** 53 | * 前序遍历 54 | */ 55 | public void preOrder() { 56 | preOrder(root, 0); 57 | } 58 | 59 | /** 60 | * 前序遍历 61 | * 62 | * @param node 63 | * @param depth 层级(用于辅助输出) 64 | */ 65 | public void preOrder(Node node, int depth) { 66 | 67 | if (node == null) { 68 | return; 69 | } 70 | 71 | // 输出当前节点 72 | this.print(node, depth); 73 | 74 | // 递归左子节点 75 | if (node.left != null) { 76 | preOrder(node.left, depth + 1); 77 | } 78 | 79 | // 递归右子节点 80 | if (node.right != null) { 81 | preOrder(node.right, depth + 1); 82 | } 83 | 84 | } 85 | 86 | /** 87 | * 中序遍历 88 | */ 89 | public void inOrder() { 90 | inOrder(root, 0); 91 | } 92 | 93 | /** 94 | * 中序遍历 95 | * 96 | * @param node 97 | * @param depth 层级(用于辅助输出) 98 | */ 99 | public void inOrder(Node node, int depth) { 100 | 101 | if (node == null) { 102 | return; 103 | } 104 | 105 | // 递归左子节点 106 | if (node.left != null) { 107 | inOrder(node.left, depth + 1); 108 | } 109 | 110 | // 输出当前节点 111 | this.print(node, depth); 112 | 113 | // 递归右子节点 114 | if (node.right != null) { 115 | inOrder(node.right, depth + 1); 116 | } 117 | 118 | } 119 | 120 | /** 121 | * 后序遍历 122 | */ 123 | public void postOrder() { 124 | postOrder(root, 0); 125 | } 126 | 127 | /** 128 | * 后序遍历 129 | * 130 | * @param node 131 | * @param depth 层级(用于辅助输出) 132 | */ 133 | public void postOrder(Node node, int depth) { 134 | 135 | if (node == null) { 136 | return; 137 | } 138 | 139 | // 递归左子节点 140 | if (node.left != null) { 141 | postOrder(node.left, depth + 1); 142 | } 143 | 144 | // 递归右子节点 145 | if (node.right != null) { 146 | postOrder(node.right, depth + 1); 147 | } 148 | 149 | // 输出当前节点 150 | this.print(node, depth); 151 | 152 | } 153 | 154 | /** 155 | * 按照层级输出节点元素 156 | * 157 | * @param node 158 | * @param depth 159 | */ 160 | private void print(Node node, int depth) { 161 | StringBuilder t = new StringBuilder(); 162 | for (int i = 0; i < depth; i++) { 163 | t.append("\t"); 164 | } 165 | System.out.printf("%s%s\n", t.toString(), node.element); 166 | } 167 | } 168 | 169 | private static class Node { 170 | private final Object element; // 节点元素 171 | private Node left; // 左子节点 172 | private Node right; // 右子节点 173 | 174 | public Node(Object element) { 175 | this.element = element; 176 | } 177 | } 178 | } 179 | -------------------------------------------------------------------------------- /wechat-develop/src/main/java/io/github/gozhuyinglong/wechatdevelop/utils/JwtUtil.java: -------------------------------------------------------------------------------- 1 | package io.github.gozhuyinglong.wechatdevelop.utils; 2 | 3 | import io.jsonwebtoken.Claims; 4 | import io.jsonwebtoken.Jwts; 5 | import io.jsonwebtoken.SignatureAlgorithm; 6 | import org.springframework.util.StringUtils; 7 | 8 | import javax.servlet.http.HttpServletRequest; 9 | import java.util.ArrayList; 10 | import java.util.Date; 11 | import java.util.List; 12 | 13 | public class JwtUtil { 14 | 15 | public static final String TOKEN_HEADER = "Authorization"; 16 | public static final String TOKEN_PREFIX = "Bearer "; 17 | public static final long TTL = 7 * 24 * 60 * 60 * 1000; 18 | public static final int TTL_COOKIE = 2 * 60 * 60; 19 | private static final String SECRET_KEY = "R325ZkDZIJ0Q+5/y8/ZwvM8NpMUhhSwzMC5ZzAUTr1s="; 20 | private static final String AUTHORITIES = "authorities"; 21 | 22 | /** 23 | * 生成 token 24 | * 25 | * @param username 26 | * @return 27 | */ 28 | public static String generateToken(String username) { 29 | return generateToken(username, new ArrayList<>()); 30 | } 31 | 32 | /** 33 | * 生成 token 34 | * 35 | * @param username 36 | * @param authorities 37 | * @return 38 | */ 39 | public static String generateToken(String username, List authorities) { 40 | return Jwts.builder() 41 | .setSubject(username) // 主题 42 | .claim(AUTHORITIES, authorities) 43 | .setIssuedAt(new Date()) // 发布时间 44 | .setExpiration(new Date(System.currentTimeMillis() + TTL)) // 到期时间 45 | .signWith(SignatureAlgorithm.HS256, SECRET_KEY) // 签名 46 | .compact(); 47 | } 48 | 49 | /** 50 | * 生成 token 51 | * 52 | * @param claims 53 | * @return 54 | */ 55 | private static String generateToken(Claims claims) { 56 | return Jwts.builder() 57 | .setClaims(claims) 58 | .setExpiration(new Date(System.currentTimeMillis() + TTL)) // 到期时间 59 | .signWith(SignatureAlgorithm.HS256, SECRET_KEY) // 签名 60 | .compact(); 61 | } 62 | 63 | /** 64 | * 解析 token 65 | * 66 | * @param token 67 | * @return 68 | */ 69 | public static Claims parseToken(String token) { 70 | return Jwts.parser().setSigningKey(SECRET_KEY).parseClaimsJws(token).getBody(); 71 | } 72 | 73 | /** 74 | * 获取 username 75 | * 76 | * @param token 77 | * @return 78 | */ 79 | public static String getUsername(String token) { 80 | return parseToken(token).getSubject(); 81 | } 82 | 83 | /** 84 | * 获取 username 85 | * 86 | * @param claims 87 | * @return 88 | */ 89 | public static String getUsername(Claims claims) { 90 | return claims.getSubject(); 91 | } 92 | 93 | /** 94 | * 是否过期 95 | * 96 | * @param token 97 | * @return 98 | */ 99 | public static boolean isExpiration(String token) { 100 | return parseToken(token).getExpiration().before(new Date()); 101 | } 102 | 103 | /** 104 | * 是否过期 105 | * 106 | * @param claims 107 | * @return 108 | */ 109 | public static boolean isExpiration(Claims claims) { 110 | return claims.getExpiration().before(new Date()); 111 | } 112 | 113 | /** 114 | * 获取角色 115 | * 116 | * @param token 117 | * @return 118 | */ 119 | public static List getAuthorities(String token) { 120 | return parseToken(token).get(AUTHORITIES, List.class); 121 | } 122 | 123 | /** 124 | * 获取角色 125 | * 126 | * @param claims 127 | * @return 128 | */ 129 | public static List getAuthorities(Claims claims) { 130 | return claims.get(AUTHORITIES, List.class); 131 | } 132 | 133 | /** 134 | * 刷新 token 135 | * 136 | * @param token 137 | * @return 138 | */ 139 | public static String refreshToken(String token) { 140 | return generateToken(parseToken(token)); 141 | } 142 | 143 | /** 144 | * 从请求头中获取Username,此方法会校验Token的有效性 145 | * 146 | * @param request 147 | * @return 148 | */ 149 | public static String getUsername(HttpServletRequest request) { 150 | String header = request.getHeader(TOKEN_HEADER); 151 | if (!StringUtils.hasText(header)) { 152 | return null; 153 | } 154 | boolean startsWith = header.startsWith(TOKEN_PREFIX); 155 | if (!startsWith) { 156 | return null; 157 | } 158 | String token = header.substring(TOKEN_PREFIX.length()); 159 | Claims claims = parseToken(token); 160 | boolean expiration = isExpiration(claims); 161 | if (expiration) { 162 | return null; 163 | } 164 | return getUsername(claims); 165 | } 166 | 167 | } 168 | -------------------------------------------------------------------------------- /java-data-structures/src/main/java/io/github/gozhuyinglong/datastructures/hashtable/HashTableDemo.java: -------------------------------------------------------------------------------- 1 | package io.github.gozhuyinglong.datastructures.hashtable; 2 | 3 | /** 4 | * 散列表 5 | * 6 | * @author 码农StayUp 7 | * @date 2021/1/18 0018 8 | */ 9 | public class HashTableDemo { 10 | 11 | public static void main(String[] args) { 12 | HashTable hashTable = new HashTable(10); 13 | System.out.println("------------初始化散列表"); 14 | hashTable.print(); 15 | 16 | System.out.println("------------添加关键字 12"); 17 | hashTable.add(12); 18 | hashTable.print(); 19 | 20 | System.out.println("------------添加关键字 13"); 21 | hashTable.add(13); 22 | hashTable.print(); 23 | 24 | System.out.println("------------添加关键字 24"); 25 | hashTable.add(24); 26 | hashTable.print(); 27 | 28 | System.out.println("------------添加关键字 34"); 29 | hashTable.add(34); 30 | hashTable.print(); 31 | 32 | System.out.println("------------添加关键字 44"); 33 | hashTable.add(44); 34 | hashTable.print(); 35 | } 36 | 37 | private static class HashTable { 38 | 39 | private final int size; // 散列表大小 40 | private final Node[] table; // 散列表 41 | 42 | private HashTable(int size) { 43 | this.size = size; 44 | this.table = new Node[size]; 45 | } 46 | 47 | /** 48 | * 散列函数 - 除留余数法 49 | * 50 | * @param key 51 | * @return 52 | */ 53 | private int hash(int key) { 54 | return key % size; 55 | } 56 | 57 | /** 58 | * 添加关键字 59 | * 60 | * @param key 61 | */ 62 | public void add(int key) { 63 | int hashAddress = hash(key); 64 | if (table[hashAddress] == null) { 65 | table[hashAddress] = new Node(key); 66 | return; 67 | } 68 | add(table[hashAddress], key); 69 | } 70 | 71 | /** 72 | * 添加关键字 - 递归 73 | * 74 | * @param node 75 | * @param key 76 | */ 77 | private void add(Node node, int key) { 78 | if (node.getNext() == null) { 79 | node.setNext(new Node(key)); 80 | return; 81 | } 82 | add(node.getNext(), key); 83 | } 84 | 85 | /** 86 | * 匹配关键字 87 | * 88 | * @param key 89 | * @return 90 | */ 91 | public boolean contains(int key) { 92 | int hashAddress = hash(key); 93 | Node headNode = table[hashAddress]; 94 | if (headNode == null) { 95 | return false; 96 | } 97 | 98 | return contains(headNode, key); 99 | } 100 | 101 | /** 102 | * 匹配关键字 - 递归 103 | * 104 | * @param node 105 | * @param key 106 | * @return 107 | */ 108 | private boolean contains(Node node, int key) { 109 | if (node == null) { 110 | return false; 111 | } 112 | if (node.getKey() == key) { 113 | return true; 114 | } 115 | return contains(node.getNext(), key); 116 | } 117 | 118 | /** 119 | * 移除关键字 120 | * 121 | * @param key 122 | */ 123 | public void remove(int key) { 124 | int hashAddress = hash(key); 125 | Node headNode = table[hashAddress]; 126 | if (headNode == null) { 127 | return; 128 | } 129 | if (headNode.getKey() == key) { 130 | table[hashAddress] = headNode.getNext(); 131 | return; 132 | } 133 | remove(headNode, key); 134 | } 135 | 136 | /** 137 | * 移除关键字 - 递归 138 | * 139 | * @param node 140 | * @param key 141 | */ 142 | private void remove(Node node, int key) { 143 | if (node.getNext() == null) { 144 | return; 145 | } 146 | if (node.getNext().getKey() == key) { 147 | node.setNext(node.getNext().getNext()); 148 | return; 149 | } 150 | remove(node.getNext(), key); 151 | } 152 | 153 | /** 154 | * 打印散列表 155 | */ 156 | public void print() { 157 | for (int i = 0; i < table.length; i++) { 158 | System.out.printf("[%s]\t", i); 159 | if (table[i] != null) { 160 | print(table[i]); 161 | } 162 | System.out.println(); 163 | } 164 | } 165 | 166 | private void print(Node node) { 167 | if (node == null) { 168 | return; 169 | } 170 | System.out.printf("%s\t", node.key); 171 | print(node.getNext()); 172 | } 173 | 174 | } 175 | 176 | 177 | private static class Node { 178 | private final int key; // 关键字 179 | private Node next; // 下一节点 180 | 181 | public Node(int key) { 182 | this.key = key; 183 | } 184 | 185 | public int getKey() { 186 | return key; 187 | } 188 | 189 | public Node getNext() { 190 | return next; 191 | } 192 | 193 | public void setNext(Node next) { 194 | this.next = next; 195 | } 196 | } 197 | } 198 | -------------------------------------------------------------------------------- /java-data-structures/src/main/java/io/github/gozhuyinglong/datastructures/linkedlist/SinglyLinkedListDemo.java: -------------------------------------------------------------------------------- 1 | package io.github.gozhuyinglong.datastructures.linkedlist; 2 | 3 | /** 4 | * 单链表 5 | * 6 | * @author 码农StayUp 7 | * @date 2020/11/20 0020 8 | */ 9 | public class SinglyLinkedListDemo { 10 | 11 | public static void main(String[] args) { 12 | Node node1 = new Node(1, "张三"); 13 | Node node2 = new Node(3, "李四"); 14 | Node node3 = new Node(7, "王五"); 15 | Node node4 = new Node(5, "赵六"); 16 | 17 | SinglyLinkedList singlyLinkedList = new SinglyLinkedList(); 18 | System.out.println("-----------添加节点(尾部)"); 19 | singlyLinkedList.add(node1); 20 | singlyLinkedList.add(node2); 21 | singlyLinkedList.add(node3); 22 | singlyLinkedList.add(node4); 23 | singlyLinkedList.print(); 24 | 25 | System.out.println("-----------获取某个节点"); 26 | Node node = singlyLinkedList.get(3); 27 | System.out.println(node); 28 | 29 | singlyLinkedList.remove(node3); 30 | System.out.println("-----------移除节点"); 31 | singlyLinkedList.print(); 32 | 33 | System.out.println("-----------修改节点"); 34 | singlyLinkedList.update(new Node(5, "赵六2")); 35 | singlyLinkedList.print(); 36 | 37 | System.out.println("-----------按顺序添加节点"); 38 | Node node5 = new Node(4, "王朝"); 39 | singlyLinkedList.addOfOrder(node5); 40 | singlyLinkedList.print(); 41 | 42 | } 43 | 44 | 45 | private static class SinglyLinkedList { 46 | 47 | // head节点是单链表的开始,不用来存储数据 48 | private Node head = new Node(0, null); 49 | 50 | /** 51 | * 将节点添加到尾部 52 | * 53 | * @param node 54 | */ 55 | public void add(Node node) { 56 | Node temp = head; 57 | while (true) { 58 | if (temp.next == null) { 59 | temp.next = node; 60 | break; 61 | } 62 | temp = temp.next; 63 | } 64 | } 65 | 66 | /** 67 | * 按顺序添加节点 68 | * 69 | * @param node 70 | */ 71 | public void addOfOrder(Node node) { 72 | Node temp = head; 73 | while (true) { 74 | if (temp.next == null) { 75 | temp.next = node; 76 | break; 77 | } else if(temp.next.key > node.getKey()){ 78 | node.next = temp.next; 79 | temp.next = node; 80 | break; 81 | } 82 | temp = temp.next; 83 | } 84 | } 85 | 86 | /** 87 | * 获取某个节点 88 | * 89 | * @param key 90 | * @return 91 | */ 92 | public Node get(int key) { 93 | if (head.next == null) { 94 | return null; 95 | } 96 | Node temp = head.next; 97 | while (temp != null) { 98 | if (temp.key == key) { 99 | return temp; 100 | } 101 | temp = temp.next; 102 | } 103 | return null; 104 | } 105 | 106 | /** 107 | * 移除一个节点 108 | * 109 | * @param node 110 | */ 111 | public void remove(Node node) { 112 | Node temp = head; 113 | while (true) { 114 | if (temp.next == null) { 115 | break; 116 | } 117 | if (temp.next.key == node.key) { 118 | temp.next = temp.next.next; 119 | break; 120 | } 121 | temp = temp.next; 122 | } 123 | } 124 | 125 | /** 126 | * 修改一个节点 127 | * 128 | * @param node 129 | */ 130 | public void update(Node node) { 131 | Node temp = head.next; 132 | while (true) { 133 | if (temp == null) { 134 | break; 135 | } 136 | if (temp.key == node.key) { 137 | temp.value = node.value; 138 | break; 139 | } 140 | temp = temp.next; 141 | } 142 | } 143 | 144 | /** 145 | * 打印链表 146 | */ 147 | public void print() { 148 | Node temp = head.next; 149 | while (temp != null) { 150 | System.out.println(temp.toString()); 151 | temp = temp.next; 152 | } 153 | } 154 | 155 | } 156 | 157 | 158 | private static class Node { 159 | private final int key; 160 | private String value; 161 | private Node next; 162 | 163 | public Node(int key, String value) { 164 | this.key = key; 165 | this.value = value; 166 | } 167 | 168 | public int getKey() { 169 | return key; 170 | } 171 | 172 | public String getValue() { 173 | return value; 174 | } 175 | 176 | public void setValue(String value) { 177 | this.value = value; 178 | } 179 | 180 | public Node getNext() { 181 | return next; 182 | } 183 | 184 | @Override 185 | public String toString() { 186 | return "Node{" + 187 | "key=" + key + 188 | ", value='" + value + '\'' + 189 | '}'; 190 | } 191 | } 192 | } 193 | -------------------------------------------------------------------------------- /java-source-analysis/src/main/java/io/github/gozhuyinglong/utils/MDUtil.java: -------------------------------------------------------------------------------- 1 | package io.github.gozhuyinglong.utils; 2 | 3 | import java.security.MessageDigest; 4 | import java.security.NoSuchAlgorithmException; 5 | 6 | /** 7 | * 消息摘要工具箱 8 | * 9 | * @author 码农StayUp 10 | * @date 2021/9/23 0023 11 | */ 12 | public class MDUtil { 13 | 14 | /** 15 | * MD5 加密 16 | * 17 | * @param data 要加密的数据 18 | * @return 32位十六进制字符串 19 | */ 20 | public static String MD5(byte[] data) { 21 | try { 22 | MessageDigest md = MessageDigest.getInstance("MD5"); 23 | byte[] bytes = md.digest(data); 24 | return bytesToHexString(bytes); 25 | } catch (NoSuchAlgorithmException e) { 26 | e.printStackTrace(); 27 | } 28 | return ""; 29 | } 30 | 31 | /** 32 | * MD5 加密 33 | * 34 | * @param data 要加密的数据 35 | * @return 32位十六进制字符串 36 | */ 37 | public static String MD5(String data) { 38 | return MD5(data.getBytes()); 39 | } 40 | 41 | /** 42 | * SHA-1 加密 43 | * 44 | * @param data 要加密的数据 45 | * @return 40位十六进制字符串 46 | */ 47 | public static String SHA1(byte[] data) { 48 | try { 49 | MessageDigest md = MessageDigest.getInstance("SHA-1"); 50 | byte[] bytes = md.digest(data); 51 | return bytesToHexString(bytes); 52 | } catch (NoSuchAlgorithmException e) { 53 | e.printStackTrace(); 54 | } 55 | return ""; 56 | } 57 | 58 | /** 59 | * SHA-1 加密 60 | * 61 | * @param data 要加密的数据 62 | * @return 40位十六进制字符串 63 | */ 64 | public static String SHA1(String data) { 65 | return SHA1(data.getBytes()); 66 | } 67 | 68 | /** 69 | * SHA-224 加密 70 | * 71 | * @param data 要加密的数据 72 | * @return 56位十六进制字符串 73 | */ 74 | public static String SHA224(byte[] data) { 75 | try { 76 | MessageDigest md = MessageDigest.getInstance("SHA-224"); 77 | byte[] bytes = md.digest(data); 78 | return bytesToHexString(bytes); 79 | } catch (NoSuchAlgorithmException e) { 80 | e.printStackTrace(); 81 | } 82 | return ""; 83 | } 84 | 85 | /** 86 | * SHA-224 加密 87 | * 88 | * @param data 要加密的数据 89 | * @return 56位十六进制字符串 90 | */ 91 | public static String SHA224(String data) { 92 | return SHA224(data.getBytes()); 93 | } 94 | 95 | /** 96 | * SHA-256 加密 97 | * 98 | * @param data 要加密的数据 99 | * @return 64位十六进制字符串 100 | */ 101 | public static String SHA256(byte[] data) { 102 | try { 103 | MessageDigest md = MessageDigest.getInstance("SHA-256"); 104 | byte[] bytes = md.digest(data); 105 | return bytesToHexString(bytes); 106 | } catch (NoSuchAlgorithmException e) { 107 | e.printStackTrace(); 108 | } 109 | return ""; 110 | } 111 | 112 | /** 113 | * SHA-256 加密 114 | * 115 | * @param data 要加密的数据 116 | * @return 64位十六进制字符串 117 | */ 118 | public static String SHA256(String data) { 119 | return SHA256(data.getBytes()); 120 | } 121 | 122 | /** 123 | * SHA-384 加密 124 | * 125 | * @param data 要加密的数据 126 | * @return 96位十六进制字符串 127 | */ 128 | public static String SHA384(byte[] data) { 129 | try { 130 | MessageDigest md = MessageDigest.getInstance("SHA-384"); 131 | byte[] bytes = md.digest(data); 132 | return bytesToHexString(bytes); 133 | } catch (NoSuchAlgorithmException e) { 134 | e.printStackTrace(); 135 | } 136 | return ""; 137 | } 138 | 139 | /** 140 | * SHA-384 加密 141 | * 142 | * @param data 要加密的数据 143 | * @return 96位十六进制字符串 144 | */ 145 | public static String SHA384(String data) { 146 | return SHA384(data.getBytes()); 147 | } 148 | 149 | /** 150 | * SHA-512 加密 151 | * 152 | * @param data 要加密的数据 153 | * @return 128位十六进制字符串 154 | */ 155 | public static String SHA512(byte[] data) { 156 | try { 157 | MessageDigest md = MessageDigest.getInstance("SHA-512"); 158 | byte[] bytes = md.digest(data); 159 | return bytesToHexString(bytes); 160 | } catch (NoSuchAlgorithmException e) { 161 | e.printStackTrace(); 162 | } 163 | return ""; 164 | } 165 | 166 | /** 167 | * SHA-512 加密 168 | * 169 | * @param data 要加密的数据 170 | * @return 128位十六进制字符串 171 | */ 172 | public static String SHA512(String data) { 173 | return SHA512(data.getBytes()); 174 | } 175 | 176 | /** 177 | * 将字节数组转换为十六进制字符串 178 | * 179 | * @param bytes 字节数组 180 | * @return 十六进制字符串 181 | */ 182 | private static String bytesToHexString(byte[] bytes) { 183 | StringBuilder hexValue = new StringBuilder(); 184 | for (byte b : bytes) { 185 | int val = b & 0xFF; 186 | if (val < 16) { 187 | hexValue.append("0"); 188 | } 189 | hexValue.append(Integer.toHexString(val)); 190 | } 191 | return hexValue.toString(); 192 | } 193 | 194 | 195 | public static void main(String[] args) { 196 | System.out.println("MD5\t\t" + MD5("123456")); 197 | System.out.println("SHA-1\t" + SHA1("123456")); 198 | System.out.println("SHA-224\t" + SHA224("123456")); 199 | System.out.println("SHA-256\t" + SHA256("123456")); 200 | System.out.println("SHA-384\t" + SHA384("123456")); 201 | System.out.println("SHA-512\t" + SHA512("123456")); 202 | } 203 | } 204 | -------------------------------------------------------------------------------- /java-source-analysis/src/main/java/io/github/gozhuyinglong/utils/AsymmetricKeyUtil.java: -------------------------------------------------------------------------------- 1 | package io.github.gozhuyinglong.utils; 2 | 3 | import javax.crypto.Cipher; 4 | import java.security.*; 5 | import java.security.spec.PKCS8EncodedKeySpec; 6 | import java.security.spec.X509EncodedKeySpec; 7 | import java.util.Base64; 8 | 9 | /** 10 | * 非对称加密工具箱 11 | * 12 | * @author 码农StayUp 13 | * @date 2021/10/15 0015 14 | */ 15 | public class AsymmetricKeyUtil { 16 | 17 | private static final String RSA_ALGORITHM = "RSA"; 18 | 19 | /** 20 | * 生成密钥对 21 | * 22 | * @param keySize 密钥大小 23 | * @return 24 | * @throws Exception 25 | */ 26 | public static KeyPair generatorRSAKeyPair(int keySize) throws Exception { 27 | // 根据 RSA 算法,获取密钥对生成器 28 | KeyPairGenerator generator = KeyPairGenerator.getInstance(RSA_ALGORITHM); 29 | // 初始化密钥长度 30 | generator.initialize(keySize); 31 | // 生成密钥对 32 | return generator.generateKeyPair(); 33 | } 34 | 35 | /** 36 | * 生成密钥对 37 | * 38 | * @param keySize 密钥大小 39 | * @return 40 | * @throws Exception 41 | */ 42 | public static KeyPairStr generatorRSAKeyPairStr(int keySize) throws Exception { 43 | // 生成密钥对 44 | KeyPair keyPair = generatorRSAKeyPair(keySize); 45 | // 获取公钥,并转码为字符串 46 | PublicKey publicKey = keyPair.getPublic(); 47 | String publicKeyStr = Base64.getEncoder().encodeToString(publicKey.getEncoded()); 48 | // 获取私钥,并转码为字符串 49 | PrivateKey privateKey = keyPair.getPrivate(); 50 | String privateKeyStr = Base64.getEncoder().encodeToString(privateKey.getEncoded()); 51 | // 返回密钥对的字符串 52 | return new KeyPairStr(publicKeyStr, privateKeyStr); 53 | } 54 | 55 | /** 56 | * 获取公钥对象 57 | * 58 | * @param publicKeyStr 59 | * @return 60 | * @throws Exception 61 | */ 62 | private static PublicKey getRSAPublicKey(String publicKeyStr) throws Exception { 63 | X509EncodedKeySpec publicKeySpec = new X509EncodedKeySpec(Base64.getDecoder().decode(publicKeyStr)); 64 | KeyFactory keyFactory = KeyFactory.getInstance(RSA_ALGORITHM); 65 | return keyFactory.generatePublic(publicKeySpec); 66 | } 67 | 68 | /** 69 | * 获取私钥对象 70 | * 71 | * @param privateKeyStr 72 | * @return 73 | * @throws Exception 74 | */ 75 | private static PrivateKey getRSAPrivateKey(String privateKeyStr) throws Exception { 76 | PKCS8EncodedKeySpec privateKeySpec = new PKCS8EncodedKeySpec(Base64.getDecoder().decode(privateKeyStr)); 77 | KeyFactory keyFactory = KeyFactory.getInstance(RSA_ALGORITHM); 78 | return keyFactory.generatePrivate(privateKeySpec); 79 | } 80 | 81 | /** 82 | * 加密 83 | * 84 | * @param data 85 | * @param key 86 | * @return 87 | */ 88 | private static String encrypt(String data, Key key) throws Exception { 89 | Cipher cipher = Cipher.getInstance(key.getAlgorithm()); 90 | cipher.init(Cipher.ENCRYPT_MODE, key); 91 | cipher.update(data.getBytes()); 92 | return Base64.getEncoder().encodeToString(cipher.doFinal()); 93 | } 94 | 95 | /** 96 | * RSA 公钥加密 97 | * 98 | * @param data 99 | * @param publicKeyStr 100 | * @return 101 | */ 102 | public static String encryptRSAPublic(String data, String publicKeyStr) { 103 | try { 104 | PublicKey publicKey = getRSAPublicKey(publicKeyStr); 105 | return encrypt(data, publicKey); 106 | } catch (Exception e) { 107 | e.printStackTrace(); 108 | throw new RuntimeException(e.getMessage()); 109 | } 110 | } 111 | 112 | 113 | /** 114 | * 解密 115 | * 116 | * @param data 117 | * @param key 118 | * @return 119 | */ 120 | private static String decrypt(String data, Key key) throws Exception { 121 | Cipher cipher = Cipher.getInstance(key.getAlgorithm()); 122 | cipher.init(Cipher.DECRYPT_MODE, key); 123 | cipher.update(Base64.getDecoder().decode(data)); 124 | return new String(cipher.doFinal()); 125 | } 126 | 127 | /** 128 | * RSA 私钥解密 129 | * 130 | * @param data 131 | * @param privateKeyStr 132 | * @return 133 | */ 134 | private static String decryptRSAPrivate(String data, String privateKeyStr) { 135 | try { 136 | PrivateKey privateKey = getRSAPrivateKey(privateKeyStr); 137 | return decrypt(data, privateKey); 138 | } catch (Exception e) { 139 | e.printStackTrace(); 140 | throw new RuntimeException(e.getMessage()); 141 | } 142 | } 143 | 144 | 145 | /** 146 | * 密钥对字符串类 147 | */ 148 | public static class KeyPairStr { 149 | private final String publicKeyStr; 150 | private final String privateKeyStr; 151 | 152 | public String getPublicKeyStr() { 153 | return publicKeyStr; 154 | } 155 | 156 | public String getPrivateKeyStr() { 157 | return privateKeyStr; 158 | } 159 | 160 | public KeyPairStr(String publicKeyStr, String privateKeyStr) { 161 | this.publicKeyStr = publicKeyStr; 162 | this.privateKeyStr = privateKeyStr; 163 | } 164 | 165 | @Override 166 | public String toString() { 167 | return "KeyPairStr{" + 168 | "publicKeyStr='" + publicKeyStr + '\'' + 169 | ", privateKeyStr='" + privateKeyStr + '\'' + 170 | '}'; 171 | } 172 | } 173 | 174 | public static void main(String[] args) throws Exception { 175 | String data = "Hello World! 我是:码农StayUp"; 176 | KeyPairStr keyPairStr = generatorRSAKeyPairStr(2048); 177 | System.out.println("公钥:" + keyPairStr.getPublicKeyStr()); 178 | System.out.println("私钥:" + keyPairStr.getPrivateKeyStr()); 179 | String text = encryptRSAPublic(data, keyPairStr.getPublicKeyStr()); 180 | System.out.println("密文:" + text); 181 | System.out.println("原文:" + decryptRSAPrivate(text, keyPairStr.getPrivateKeyStr())); 182 | } 183 | } 184 | -------------------------------------------------------------------------------- /java-data-structures/src/main/java/io/github/gozhuyinglong/datastructures/linkedlist/DoublyLinkedListDemo.java: -------------------------------------------------------------------------------- 1 | package io.github.gozhuyinglong.datastructures.linkedlist; 2 | 3 | /** 4 | * 双链表 5 | * 6 | * @author 码农StayUp 7 | * @date 2020/11/21 0021 8 | */ 9 | public class DoublyLinkedListDemo { 10 | 11 | public static void main(String[] args) { 12 | 13 | DoublyLinkedList doublyLinkedList = new DoublyLinkedList(); 14 | 15 | System.out.println("-----------从尾部添加节点"); 16 | doublyLinkedList 17 | .addToTail(new Node(1, "张三")) 18 | .addToTail(new Node(3, "李四")) 19 | .addToTail(new Node(7, "王五")) 20 | .addToTail(new Node(5, "赵六")) 21 | .print(); 22 | 23 | System.out.println("-----------从头部添加节点"); 24 | doublyLinkedList 25 | .addToHead(new Node(0, "朱开山")) 26 | .print(); 27 | 28 | System.out.println("-----------获取某个节点"); 29 | System.out.println(doublyLinkedList.get(3)); 30 | 31 | System.out.println("-----------移除节点"); 32 | doublyLinkedList 33 | .remove(new Node(3, "李四")) 34 | .print(); 35 | 36 | System.out.println("-----------修改节点"); 37 | doublyLinkedList 38 | .update(new Node(5, "赵六2")).print(); 39 | 40 | System.out.println("-----------按顺序添加节点"); 41 | doublyLinkedList 42 | .addOfOrder(new Node(4, "王朝")) 43 | .print(); 44 | } 45 | 46 | private static class DoublyLinkedList { 47 | 48 | private Node first = null; // first节点是双链表的头部,即第一个节点 49 | private Node last = null; // tail节点是双链表的尾部,即最后一个节点 50 | 51 | /** 52 | * 从尾部添加 53 | * 54 | * @param node 55 | */ 56 | public DoublyLinkedList addToTail(Node node) { 57 | if (last == null) { 58 | first = node; 59 | } else { 60 | last.next = node; 61 | node.prev = last; 62 | } 63 | last = node; 64 | return this; 65 | } 66 | 67 | /** 68 | * 按照顺序添加 69 | * 70 | * @param node 71 | */ 72 | public DoublyLinkedList addOfOrder(Node node) { 73 | if (first == null) { 74 | first = node; 75 | last = node; 76 | return this; 77 | } 78 | 79 | // node比头节点小,将node设为头节点 80 | if (first.key > node.key) { 81 | first.prev = node; 82 | node.next = first; 83 | first = node; 84 | return this; 85 | } 86 | 87 | // node比尾节点大,将node设为尾节点 88 | if (last.key < node.key) { 89 | last.next = node; 90 | node.prev = last; 91 | last = node; 92 | return this; 93 | } 94 | 95 | Node temp = first.next; 96 | while (true) { 97 | if (temp.key > node.key) { 98 | node.next = temp; 99 | node.prev = temp.prev; 100 | temp.prev.next = node; 101 | temp.prev = node; 102 | break; 103 | } 104 | temp = temp.next; 105 | } 106 | return this; 107 | } 108 | 109 | /** 110 | * 从头部添加 111 | * 112 | * @param node 113 | */ 114 | public DoublyLinkedList addToHead(Node node) { 115 | if (first == null) { 116 | last = node; 117 | } else { 118 | node.next = first; 119 | first.prev = node; 120 | } 121 | first = node; 122 | return this; 123 | } 124 | 125 | /** 126 | * 获取节点 127 | * 128 | * @param key 129 | * @return 130 | */ 131 | public Node get(int key) { 132 | if (first == null) { 133 | return null; 134 | } 135 | Node temp = first; 136 | while (temp != null) { 137 | if (temp.key == key) { 138 | return temp; 139 | } 140 | temp = temp.next; 141 | } 142 | return null; 143 | } 144 | 145 | /** 146 | * 移除节点 147 | * 148 | * @param node 149 | */ 150 | public DoublyLinkedList remove(Node node) { 151 | if (first == null) { 152 | return this; 153 | } 154 | // 要移除的是头节点 155 | if (first == node) { 156 | first.next.prev = null; 157 | first = first.next; 158 | return this; 159 | } 160 | // 要移除的是尾节点 161 | if (last == node) { 162 | last.prev.next = null; 163 | last = last.prev; 164 | return this; 165 | } 166 | 167 | Node temp = first.next; 168 | while (temp != null) { 169 | if (temp.key == node.key) { 170 | temp.prev.next = temp.next; 171 | temp.next.prev = temp.prev; 172 | break; 173 | } 174 | temp = temp.next; 175 | } 176 | return this; 177 | } 178 | 179 | /** 180 | * 修改某个节点 181 | * 182 | * @param node 183 | */ 184 | public DoublyLinkedList update(Node node) { 185 | if (first == null) { 186 | return this; 187 | } 188 | 189 | Node temp = first; 190 | while (temp != null) { 191 | if (temp.key == node.key) { 192 | temp.value = node.value; 193 | break; 194 | } 195 | temp = temp.next; 196 | } 197 | return this; 198 | } 199 | 200 | /** 201 | * 打印链表 202 | */ 203 | public void print() { 204 | if (first == null) { 205 | return; 206 | } 207 | Node temp = first; 208 | while (temp != null) { 209 | System.out.println(temp); 210 | temp = temp.next; 211 | } 212 | } 213 | } 214 | 215 | private static class Node { 216 | private final int key; 217 | private String value; 218 | private Node prev; // 指向上一节点 219 | private Node next; // 指向下一节点 220 | 221 | public Node(int key, String value) { 222 | this.key = key; 223 | this.value = value; 224 | } 225 | 226 | @Override 227 | public String toString() { 228 | return "Node{" + 229 | "key=" + key + 230 | ", value='" + value + '\'' + 231 | '}'; 232 | } 233 | } 234 | } 235 | -------------------------------------------------------------------------------- /java-source-analysis/src/main/java/io/github/gozhuyinglong/utils/SymmetricKeyUtil.java: -------------------------------------------------------------------------------- 1 | package io.github.gozhuyinglong.utils; 2 | 3 | import javax.crypto.Cipher; 4 | import javax.crypto.KeyGenerator; 5 | import javax.crypto.spec.IvParameterSpec; 6 | import java.security.Key; 7 | import java.security.SecureRandom; 8 | import java.util.Base64; 9 | 10 | /** 11 | * 对称加密工具箱 12 | * 13 | * @author 码农StayUp 14 | * @date 2021/10/13 0013 15 | */ 16 | public class SymmetricKeyUtil { 17 | 18 | /** 19 | * DES 算法名称 20 | */ 21 | private static final String DES_ALGORITHM = "DES"; 22 | /** 23 | * AES 算法名称 24 | */ 25 | private static final String AES_ALGORITHM = "AES"; 26 | /** 27 | * DES 算法的转换类型(算法名称/工作模式/填充方式) 28 | */ 29 | private static final String DES_TRANSFORMATION = "DES/ECB/PKCS5Padding"; 30 | /** 31 | * AES 算法的转换类型(算法名称/工作模式/填充方式) 32 | */ 33 | private static final String AES_TRANSFORMATION = "AES/CBC/PKCS5Padding"; 34 | /** 35 | * DES 初始化向量,固定8位字节 36 | */ 37 | private static final String DES_IV_PARAMETER = "12345678"; 38 | /** 39 | * AES 初始化向量,固定16位字节 40 | */ 41 | private static final String AES_IV_PARAMETER = "1234567812345678"; 42 | 43 | /** 44 | * 通过密码和算法获取 Key 对象 45 | * 46 | * @param key 密钥 47 | * @param algorithm 算法,例如:AES (128)、DES (56)、DESede (168)、HmacSHA1、HmacSHA256 48 | * @return 密钥 Key 49 | * @throws Exception 50 | */ 51 | private static Key getKey(byte[] key, String algorithm) throws Exception { 52 | // 通过算法获取 KeyGenerator 对象 53 | KeyGenerator keyGenerator = KeyGenerator.getInstance(algorithm); 54 | // 使用密钥做为随机数,初始化 KeyGenerator 对象 55 | keyGenerator.init(new SecureRandom(key)); 56 | // 生成 Key 57 | return keyGenerator.generateKey(); 58 | } 59 | 60 | /** 61 | * DES 加密 62 | * 63 | * @param data 原始数据 64 | * @param key 密钥 65 | * @return 密文 66 | */ 67 | private static byte[] encryptDES(byte[] data, byte[] key) throws Exception { 68 | // 获取 DES Key 69 | Key secretKey = getKey(key, DES_ALGORITHM); 70 | 71 | // 通过标准转换获取 Cipher 对象, 由该对象完成实际的加密操作 72 | Cipher cipher = Cipher.getInstance(DES_TRANSFORMATION); 73 | // 通过加密模式、密钥,初始化 Cipher 对象 74 | cipher.init(Cipher.ENCRYPT_MODE, secretKey); 75 | // 生成密文 76 | return cipher.doFinal(data); 77 | } 78 | 79 | /** 80 | * DES 加密 81 | * 82 | * @param data 原始数据 83 | * @param key 密钥 84 | * @return 密文(Base64) 85 | */ 86 | public static String encryptDES(String data, String key) { 87 | try { 88 | byte[] bytes = encryptDES(data.getBytes(), key.getBytes()); 89 | return new String(Base64.getEncoder().encode(bytes)); 90 | } catch (Exception e) { 91 | e.printStackTrace(); 92 | } 93 | return null; 94 | } 95 | 96 | /** 97 | * DES 解密 98 | * 99 | * @param data 密文 100 | * @param key 密钥 101 | * @return 原始数据 102 | */ 103 | private static byte[] decryptDES(byte[] data, byte[] key) throws Exception { 104 | // 获取 DES Key 105 | Key secretKey = getKey(key, DES_ALGORITHM); 106 | // 通过标准转换获取 Cipher 对象, 由该对象完成实际的加密操作 107 | Cipher cipher = Cipher.getInstance(DES_TRANSFORMATION); 108 | // 通过加密模式、密钥,初始化 Cipher 对象 109 | cipher.init(Cipher.DECRYPT_MODE, secretKey); 110 | // 生成密文 111 | return cipher.doFinal(data); 112 | } 113 | 114 | /** 115 | * DES 解密 116 | * 117 | * @param data 密文 118 | * @param key 密钥 119 | * @return 原始数据 120 | */ 121 | public static String decryptDES(String data, String key) { 122 | try { 123 | byte[] bytes = Base64.getDecoder().decode(data.getBytes()); 124 | return new String(decryptDES(bytes, key.getBytes())); 125 | } catch (Exception e) { 126 | e.printStackTrace(); 127 | } 128 | return null; 129 | } 130 | 131 | /** 132 | * AES 加密 133 | * 134 | * @param data 原始数据 135 | * @param key 密钥 136 | * @return 密文 137 | * @throws Exception 138 | */ 139 | private static byte[] encryptAES(byte[] data, byte[] key) throws Exception { 140 | // 获取 AES Key 141 | Key secretKey = getKey(key, AES_ALGORITHM); 142 | 143 | // 通过标准转换获取 Cipher 对象, 由该对象完成实际的加密操作 144 | Cipher cipher = Cipher.getInstance(AES_TRANSFORMATION); 145 | // 创建一个 IV 对象(CBC 模式需要使用初始化向量) 146 | IvParameterSpec iv = new IvParameterSpec(AES_IV_PARAMETER.getBytes()); 147 | // 通过加密模式、密钥和 IV,初始化 Cipher 对象 148 | cipher.init(Cipher.ENCRYPT_MODE, secretKey, iv); 149 | // 生成密文 150 | return cipher.doFinal(data); 151 | } 152 | 153 | /** 154 | * AES 加密 155 | * 156 | * @param data 原始数据 157 | * @param key 密钥 158 | * @return 密文 159 | */ 160 | public static String encryptAES(String data, String key) { 161 | try { 162 | byte[] bytes = encryptAES(data.getBytes(), key.getBytes()); 163 | return new String(Base64.getEncoder().encode(bytes)); 164 | } catch (Exception e) { 165 | e.printStackTrace(); 166 | } 167 | return null; 168 | } 169 | 170 | /** 171 | * AES 解密 172 | * 173 | * @param data 密文 174 | * @param key 密钥 175 | * @return 原始数据 176 | * @throws Exception 177 | */ 178 | private static byte[] decryptAES(byte[] data, byte[] key) throws Exception { 179 | // 获取 AES Key 180 | Key secretKey = getKey(key, AES_ALGORITHM); 181 | // 通过标准转换获取 Cipher 对象, 由该对象完成实际的加密操作 182 | Cipher cipher = Cipher.getInstance(AES_TRANSFORMATION); 183 | // 创建一个 IV 对象(CBC 模式需要使用初始化向量) 184 | IvParameterSpec iv = new IvParameterSpec(AES_IV_PARAMETER.getBytes()); 185 | // 通过加密模式、密钥和 IV,初始化 Cipher 对象 186 | cipher.init(Cipher.DECRYPT_MODE, secretKey, iv); 187 | // 生成密文 188 | return cipher.doFinal(data); 189 | } 190 | 191 | /** 192 | * AES 解密 193 | * 194 | * @param data 密文 195 | * @param key 密钥 196 | * @return 原始数据 197 | */ 198 | public static String decryptAES(String data, String key) { 199 | try { 200 | byte[] bytes = Base64.getDecoder().decode(data.getBytes()); 201 | return new String(decryptAES(bytes, key.getBytes())); 202 | } catch (Exception e) { 203 | e.printStackTrace(); 204 | } 205 | return null; 206 | } 207 | 208 | public static void main(String[] args) { 209 | String data = "Hello World! 我是:码农StayUp"; 210 | String key = "12345678"; 211 | 212 | String cipherTextDES = encryptDES(data, key); 213 | System.out.println(cipherTextDES); 214 | System.out.println(decryptDES(cipherTextDES, key)); 215 | 216 | String cipherTextAES = encryptAES(data, key); 217 | System.out.println(cipherTextAES); 218 | System.out.println(decryptAES(cipherTextAES, key)); 219 | } 220 | } 221 | -------------------------------------------------------------------------------- /java-data-structures/src/main/java/io/github/gozhuyinglong/datastructures/tree/BinarySearchTreeDemo.java: -------------------------------------------------------------------------------- 1 | package io.github.gozhuyinglong.datastructures.tree; 2 | 3 | /** 4 | * 二叉查找树(二叉排序树) 5 | * 6 | * @author 码农StayUp 7 | * @date 2020/12/3 0003 8 | */ 9 | public class BinarySearchTreeDemo { 10 | 11 | public static void main(String[] args) { 12 | BinarySearchTree tree = new BinarySearchTree(); 13 | 14 | System.out.println("----------------------添加元素"); 15 | Integer[] array = {5, 2, 7, 1, 4, 3, 7, 6, 9, 8}; 16 | for (Integer element : array) { 17 | System.out.printf("添加元素[%s] --> %s\n", element, tree.add(element)); 18 | } 19 | 20 | System.out.println("----------------------顺序输出(中序遍历)"); 21 | tree.orderPrint(); 22 | 23 | System.out.println("----------------------查找元素"); 24 | System.out.println(tree.find(7)); 25 | 26 | System.out.println("----------------------查找最小元素"); 27 | System.out.println(tree.findMin()); 28 | 29 | System.out.println("----------------------查找最大元素"); 30 | System.out.println(tree.findMax()); 31 | 32 | System.out.println("----------------------是否包含元素"); 33 | System.out.println("是否包含[0] --> \t" + tree.contains(0)); 34 | System.out.println("是否包含[2] --> \t" + tree.contains(2)); 35 | 36 | System.out.println("----------------------删除目标元素"); 37 | System.out.println("删除[0] --> \t" + tree.remove(0)); 38 | tree.orderPrint(); 39 | System.out.println("删除[1] --> \t" + tree.remove(1)); 40 | tree.orderPrint(); 41 | System.out.println("删除[4] --> \t" + tree.remove(4)); 42 | tree.orderPrint(); 43 | System.out.println("删除[7] --> \t" + tree.remove(7)); 44 | tree.orderPrint(); 45 | 46 | } 47 | 48 | private static class BinarySearchTree { 49 | private Node root; // 树根 50 | 51 | /** 52 | * 添加元素 53 | * 54 | * @param element 55 | * @return 56 | */ 57 | public boolean add(int element) { 58 | if (root == null) { 59 | root = new Node(element); 60 | return true; 61 | } 62 | return add(root, element); 63 | } 64 | 65 | /** 66 | * 添加元素(递归) 67 | * 68 | * @param node 69 | * @param element 70 | * @return 71 | */ 72 | private boolean add(Node node, int element) { 73 | if (node.compareTo(element) < 0) { 74 | if (node.left == null) { 75 | node.left = new Node(element); 76 | return true; 77 | } else { 78 | return add(node.left, element); 79 | } 80 | } else if (node.compareTo(element) > 0) { 81 | if (node.right == null) { 82 | node.right = new Node(element); 83 | return true; 84 | } else { 85 | return add(node.right, element); 86 | } 87 | } else { 88 | return false; 89 | } 90 | } 91 | 92 | /** 93 | * 查询元素 94 | * 95 | * @param element 96 | * @return 97 | */ 98 | public Node find(int element) { 99 | if (root == null) { 100 | return null; 101 | } 102 | return find(root, element); 103 | } 104 | 105 | /** 106 | * 查询元素(递归) 107 | * 108 | * @param node 109 | * @param element 110 | * @return 111 | */ 112 | private Node find(Node node, int element) { 113 | if (node == null) { 114 | return null; 115 | } 116 | int compareResult = node.compareTo(element); 117 | if (compareResult < 0) { 118 | return find(node.left, element); 119 | } else if (compareResult > 0) { 120 | return find(node.right, element); 121 | } else { 122 | return node; 123 | } 124 | } 125 | 126 | /** 127 | * 查找最大值 128 | * 129 | * @return 130 | */ 131 | public Node findMax() { 132 | return findMax(root); 133 | } 134 | 135 | /** 136 | * 查找最大值(递归) 137 | * 138 | * @param node 139 | * @return 140 | */ 141 | private Node findMax(Node node) { 142 | if (node.right == null) { 143 | return node; 144 | } 145 | return findMax(node.right); 146 | } 147 | 148 | /** 149 | * 查找最小值 150 | * 151 | * @return 152 | */ 153 | private Node findMin() { 154 | return findMin(root); 155 | } 156 | 157 | /** 158 | * 查找最小值(递归) 159 | * 160 | * @param node 161 | * @return 162 | */ 163 | private Node findMin(Node node) { 164 | if (node.left == null) { 165 | return node; 166 | } 167 | return findMin(node.left); 168 | } 169 | 170 | /** 171 | * 顺序输出 172 | */ 173 | public void orderPrint() { 174 | orderPrint(root); 175 | } 176 | 177 | 178 | /** 179 | * 顺序输出(递归) 180 | * 181 | * @param node 182 | */ 183 | private void orderPrint(Node node) { 184 | 185 | if (node == null) { 186 | return; 187 | } 188 | 189 | // 递归左子节点 190 | if (node.left != null) { 191 | orderPrint(node.left); 192 | } 193 | 194 | // 输出当前节点 195 | System.out.println(node.element); 196 | 197 | // 递归右子节点 198 | if (node.right != null) { 199 | orderPrint(node.right); 200 | } 201 | 202 | } 203 | 204 | /** 205 | * 是否包含某值 206 | * 207 | * @param element 208 | * @return 209 | */ 210 | public boolean contains(int element) { 211 | if (find(element) == null) { 212 | return false; 213 | } 214 | return true; 215 | } 216 | 217 | /** 218 | * 删除目标元素 219 | * 220 | * @param element 221 | * @return 222 | */ 223 | public boolean remove(int element) { 224 | if (root == null) { 225 | return false; 226 | } 227 | // 如果删除的元素是root 228 | if (root.compareTo(element) == 0) { 229 | if (root.right == null) { 230 | root = root.left; 231 | } else { 232 | root.right.left = root.left; 233 | root = root.right; 234 | } 235 | return true; 236 | } 237 | return remove(null, root, element); 238 | } 239 | 240 | /** 241 | * 删除目标元素(递归),有三种情况: 242 | * (1)目标元素为叶子节点 243 | * (2)目标元素即有左子树,也有右子树 244 | * (3)目标元素只有左子树,或只有右子树 245 | * 246 | * @param parentNode 当前节点的父节点 247 | * @param node 当前节点(若当前节点上的元素与要删除的元素匹配,则删除当前节点) 248 | * @param element 要删除的元素 249 | * @return 250 | */ 251 | private boolean remove(Node parentNode, Node node, int element) { 252 | if (node == null) { 253 | return false; 254 | } 255 | // 先找到目标元素 256 | int compareResult = node.compareTo(element); 257 | if (compareResult < 0) { 258 | return remove(node, node.left, element); 259 | } 260 | if (compareResult > 0) { 261 | return remove(node, node.right, element); 262 | } 263 | 264 | // 找到目标元素,判断该节点是父节点的左子树还是右子树 265 | boolean isLeftOfParent = false; 266 | if (parentNode.left != null && parentNode.left.compareTo(element) == 0) { 267 | isLeftOfParent = true; 268 | } 269 | 270 | // 删除目标元素 271 | if (node.left == null && node.right == null) { // (1)目标元素为叶子节点,直接删除 272 | if (isLeftOfParent) { 273 | parentNode.left = null; 274 | } else { 275 | parentNode.right = null; 276 | } 277 | } else if (node.left != null && node.right != null) { // (2)目标元素即有左子树,也有右子树 278 | // 找到右子树最小值(叶子节点),并将其删除 279 | Node minNode = findMin(node.right); 280 | remove(minNode.element); 281 | // 将该最小值替换要删除的目标节点 282 | minNode.left = node.left; 283 | minNode.right = node.right; 284 | if(isLeftOfParent) { 285 | parentNode.left = minNode; 286 | } else { 287 | parentNode.right = minNode; 288 | } 289 | 290 | } else { // (3)目标元素只有左子树,或只有右子树 291 | if (isLeftOfParent) { 292 | parentNode.left = node.left != null ? node.left : node.right; 293 | } else { 294 | parentNode.right = node.left != null ? node.left : node.right; 295 | } 296 | } 297 | return true; 298 | } 299 | } 300 | 301 | private static class Node implements Comparable { 302 | private final Integer element; // 数据元素 303 | private Node left; // 左子树 304 | private Node right; // 右子树 305 | 306 | private Node(Integer element) { 307 | this.element = element; 308 | } 309 | 310 | @Override 311 | public int compareTo(Integer o) { 312 | return o.compareTo(element); 313 | } 314 | 315 | @Override 316 | public String toString() { 317 | return "Node{" + 318 | "element=" + element + 319 | '}'; 320 | } 321 | } 322 | } 323 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /java-data-structures/src/main/java/io/github/gozhuyinglong/datastructures/tree/AVLTreeDemo.java: -------------------------------------------------------------------------------- 1 | package io.github.gozhuyinglong.datastructures.tree; 2 | 3 | 4 | /** 5 | * AVL树(平衡二叉树) 6 | * 7 | * @author 码农StayUp 8 | * @date 2020/12/14 0014 9 | */ 10 | public class AVLTreeDemo { 11 | 12 | public static void main(String[] args) { 13 | System.out.println("----------------------添加元素"); 14 | int[] array = {8, 5, 9, 3, 6, 7}; 15 | AVLTree tree = new AVLTree(); 16 | for (int element : array) { 17 | System.out.printf("添加元素[%s]\n", element); 18 | tree.add(element); 19 | } 20 | 21 | System.out.println("----------------------顺序输出(中序遍历)"); 22 | tree.orderPrint(); 23 | 24 | System.out.println("----------------------当前树的高度"); 25 | System.out.println("当前树:" + tree.getRoot().height()); 26 | System.out.println("左子树:" + tree.getRoot().leftHeight()); 27 | System.out.println("右子树:" + tree.getRoot().rightHeight()); 28 | 29 | System.out.println("----------------------删除元素"); 30 | tree.remove(3); 31 | System.out.println("----------------------当前树的高度"); 32 | System.out.println("当前树:" + tree.getRoot().height()); 33 | System.out.println("左子树:" + tree.getRoot().leftHeight()); 34 | System.out.println("右子树:" + tree.getRoot().rightHeight()); 35 | 36 | System.out.println("----------------------删除元素"); 37 | tree.remove(5); 38 | System.out.println("----------------------当前树的高度"); 39 | System.out.println("当前树:" + tree.getRoot().height()); 40 | System.out.println("左子树:" + tree.getRoot().leftHeight()); 41 | System.out.println("右子树:" + tree.getRoot().rightHeight()); 42 | } 43 | 44 | private static class AVLTree { 45 | private Node root; // 树根 46 | 47 | public Node getRoot() { 48 | return root; 49 | } 50 | 51 | /** 52 | * 添加元素 53 | * 54 | * @param element 55 | * @return 56 | */ 57 | public void add(int element) { 58 | if (root == null) { 59 | root = new Node(element); 60 | return; 61 | } 62 | add(root, element); 63 | } 64 | 65 | /** 66 | * 添加元素(递归) 67 | * 68 | * @param node 69 | * @param element 70 | * @return 71 | */ 72 | private void add(Node node, int element) { 73 | if (node.compareTo(element) < 0) { 74 | if (node.left == null) { 75 | node.left = new Node(element); 76 | } else { 77 | add(node.left, element); 78 | } 79 | } else if (node.compareTo(element) > 0) { 80 | if (node.right == null) { 81 | node.right = new Node(element); 82 | } else { 83 | add(node.right, element); 84 | } 85 | } 86 | balance(node); 87 | } 88 | 89 | /** 90 | * 查询元素 91 | * 92 | * @param element 93 | * @return 94 | */ 95 | public Node find(int element) { 96 | if (root == null) { 97 | return null; 98 | } 99 | return find(root, element); 100 | } 101 | 102 | /** 103 | * 查询元素(递归) 104 | * 105 | * @param node 106 | * @param element 107 | * @return 108 | */ 109 | private Node find(Node node, int element) { 110 | if (node == null) { 111 | return null; 112 | } 113 | int compareResult = node.compareTo(element); 114 | if (compareResult < 0) { 115 | return find(node.left, element); 116 | } else if (compareResult > 0) { 117 | return find(node.right, element); 118 | } else { 119 | return node; 120 | } 121 | } 122 | 123 | /** 124 | * 查找最大值 125 | * 126 | * @return 127 | */ 128 | public Node findMax() { 129 | return findMax(root); 130 | } 131 | 132 | /** 133 | * 查找最大值(递归) 134 | * 135 | * @param node 136 | * @return 137 | */ 138 | private Node findMax(Node node) { 139 | if (node.right == null) { 140 | return node; 141 | } 142 | return findMax(node.right); 143 | } 144 | 145 | /** 146 | * 查找最小值 147 | * 148 | * @return 149 | */ 150 | private Node findMin() { 151 | return findMin(root); 152 | } 153 | 154 | /** 155 | * 查找最小值(递归) 156 | * 157 | * @param node 158 | * @return 159 | */ 160 | private Node findMin(Node node) { 161 | if (node.left == null) { 162 | return node; 163 | } 164 | return findMin(node.left); 165 | } 166 | 167 | /** 168 | * 顺序输出 169 | */ 170 | public void orderPrint() { 171 | orderPrint(root); 172 | } 173 | 174 | 175 | /** 176 | * 顺序输出(递归) 177 | * 178 | * @param node 179 | */ 180 | private void orderPrint(Node node) { 181 | 182 | if (node == null) { 183 | return; 184 | } 185 | 186 | // 递归左子节点 187 | if (node.left != null) { 188 | orderPrint(node.left); 189 | } 190 | 191 | // 输出当前节点 192 | System.out.println(node.element); 193 | 194 | // 递归右子节点 195 | if (node.right != null) { 196 | orderPrint(node.right); 197 | } 198 | 199 | } 200 | 201 | /** 202 | * 是否包含某值 203 | * 204 | * @param element 205 | * @return 206 | */ 207 | public boolean contains(int element) { 208 | if (find(element) == null) { 209 | return false; 210 | } 211 | return true; 212 | } 213 | 214 | /** 215 | * 删除目标元素 216 | * 217 | * @param element 218 | * @return 219 | */ 220 | public void remove(int element) { 221 | if (root == null) { 222 | return; 223 | } 224 | // 如果删除的元素是root 225 | if (root.compareTo(element) == 0) { 226 | if (root.right == null) { 227 | root = root.left; 228 | } else { 229 | root.right.left = root.left; 230 | root = root.right; 231 | } 232 | balance(root); 233 | return; 234 | } 235 | remove(null, root, element); 236 | } 237 | 238 | /** 239 | * 删除目标元素(递归),有三种情况: 240 | * (1)目标元素为叶子节点 241 | * (2)目标元素即有左子树,也有右子树 242 | * (3)目标元素只有左子树,或只有右子树 243 | * 244 | * @param parentNode 当前节点的父节点 245 | * @param node 当前节点(若当前节点上的元素与要删除的元素匹配,则删除当前节点) 246 | * @param element 要删除的元素 247 | * @return 248 | */ 249 | private void remove(Node parentNode, Node node, int element) { 250 | if (node == null) { 251 | return; 252 | } 253 | // 先找到目标元素 254 | int compareResult = node.compareTo(element); 255 | if (compareResult < 0) { 256 | remove(node, node.left, element); 257 | } else if (compareResult > 0) { 258 | remove(node, node.right, element); 259 | } else { 260 | // 找到目标元素,判断该节点是父节点的左子树还是右子树 261 | boolean isLeftOfParent = false; 262 | if (parentNode.left != null && parentNode.left.compareTo(element) == 0) { 263 | isLeftOfParent = true; 264 | } 265 | 266 | // 删除目标元素 267 | if (node.left == null && node.right == null) { // (1)目标元素为叶子节点,直接删除 268 | if (isLeftOfParent) { 269 | parentNode.left = null; 270 | } else { 271 | parentNode.right = null; 272 | } 273 | } else if (node.left != null && node.right != null) { // (2)目标元素即有左子树,也有右子树 274 | // 找到右子树最小值(叶子节点),并将其删除 275 | Node minNode = findMin(node.right); 276 | remove(minNode.element); 277 | // 将该最小值替换要删除的目标节点 278 | minNode.left = node.left; 279 | minNode.right = node.right; 280 | if (isLeftOfParent) { 281 | parentNode.left = minNode; 282 | } else { 283 | parentNode.right = minNode; 284 | } 285 | 286 | } else { // (3)目标元素只有左子树,或只有右子树 287 | if (isLeftOfParent) { 288 | parentNode.left = node.left != null ? node.left : node.right; 289 | } else { 290 | parentNode.right = node.left != null ? node.left : node.right; 291 | } 292 | } 293 | } 294 | balance(node); 295 | } 296 | 297 | /** 298 | * 通过旋转对做进行平衡 299 | * 300 | * @param node 301 | */ 302 | public void balance(Node node) { 303 | 304 | if (node == null) { 305 | return; 306 | } 307 | 308 | if (node.leftHeight() - node.rightHeight() > 1) { 309 | if (node.left.rightHeight() > node.left.leftHeight()) { 310 | node.left.leftRotate(); 311 | } 312 | node.rightRotate(); 313 | 314 | } else if (node.rightHeight() - node.leftHeight() > 1) { 315 | if (node.right.leftHeight() > node.right.rightHeight()) { 316 | node.right.rightHeight(); 317 | } 318 | node.leftRotate(); 319 | } 320 | 321 | } 322 | } 323 | 324 | private static class Node implements Comparable { 325 | private int element; // 数据元素 326 | private Node left; // 左子节点 327 | private Node right; // 右子节点 328 | 329 | public Node(int element) { 330 | this.element = element; 331 | } 332 | 333 | /** 334 | * 以当前节点为根的高度 335 | * 336 | * @return 337 | */ 338 | public int height() { 339 | return Math.max(left == null ? 0 : left.height(), right == null ? 0 : right.height()) + 1; 340 | } 341 | 342 | /** 343 | * 当前节点的左子树高度 344 | * 345 | * @return 346 | */ 347 | public int leftHeight() { 348 | if (left == null) { 349 | return 0; 350 | } 351 | return left.height(); 352 | } 353 | 354 | /** 355 | * 当前节点的右子树高度 356 | * 357 | * @return 358 | */ 359 | public int rightHeight() { 360 | if (right == null) { 361 | return 0; 362 | } 363 | return right.height(); 364 | } 365 | 366 | /** 367 | * 左旋转(向左旋转) 368 | *

369 | * 以当前节点为树根,当(右子树的高度 - 左子树的高度) > 1时,进行左旋转: 370 | * (1)将当前(根)节点向左下移,成为新的左子节点;并将其右子节点设为原根节点右子树的左子树 371 | * (2)将当前节点的右节点上移,成新的树根(当前节点);并将左子节点指向新的左子节点(原树根) 372 | */ 373 | public void leftRotate() { 374 | // 将当前节点向左下移,成为新的左节点 375 | Node newLeftNode = new Node(element); 376 | newLeftNode.left = left; 377 | // 将右子节点设为原根节点右子树的左子树 378 | newLeftNode.right = right.left; 379 | 380 | // 将右节点上移,成为新的树根(当前节点) 381 | element = right.element; 382 | // 将左子节点设为新的左子节点(原树根) 383 | left = newLeftNode; 384 | right = right.right; 385 | 386 | } 387 | 388 | /** 389 | * 右旋转(向右旋转) 390 | *

391 | * 以当前节点为树根,当(左子树的高度 - 右子树的高度) > 1时,进行右旋转: 392 | * (1)将当前(根)节点向右下移,成为新的右子节点;并将其左子节点设为原根节点左子树的右子树 393 | * (2)将当前节点的右节点上移,成为新的树根(当前节点);并将右子节点指向新的右子节点(原树根) 394 | */ 395 | public void rightRotate() { 396 | // 将当前节点向右下移,成为新的右子节点 397 | Node newRightNode = new Node(element); 398 | // 将左子节点指向原根节点的左子树的右子树 399 | newRightNode.left = left.right; 400 | newRightNode.right = right; 401 | 402 | // 将左子节点上移,成为新的树根(当前节点) 403 | element = left.element; 404 | left = left.left; 405 | // 将右子节点设为新的右子节点(原树根) 406 | right = newRightNode; 407 | } 408 | 409 | @Override 410 | public int compareTo(Integer o) { 411 | return o.compareTo(element); 412 | } 413 | 414 | @Override 415 | public String toString() { 416 | return "Node{" + 417 | "element=" + element + 418 | '}'; 419 | } 420 | } 421 | } 422 | -------------------------------------------------------------------------------- /java-source-analysis/src/main/java/io/github/gozhuyinglong/reflection/ReflectionTest.java: -------------------------------------------------------------------------------- 1 | package io.github.gozhuyinglong.reflection; 2 | 3 | import org.junit.Test; 4 | 5 | import java.lang.annotation.Annotation; 6 | import java.lang.reflect.*; 7 | 8 | /** 9 | * @author 码农StayUp 10 | * @date 2021/2/2 0002 11 | */ 12 | public class ReflectionTest { 13 | 14 | /** 15 | * 获取 Class 类实例的四种方式 16 | */ 17 | @Test 18 | public void testClassFor() { 19 | 20 | // 1.通过类实例获取 21 | Person person = new Person(); 22 | Class clazz1 = person.getClass(); 23 | System.out.println("01 - " + clazz1); 24 | 25 | // 2.通过类直接调用class获取 26 | Class clazz2 = Person.class; 27 | System.out.println("02 - " + clazz2); 28 | 29 | // 3.通过Class.forName获取 30 | Class clazz3 = null; 31 | try { 32 | clazz3 = Class.forName("io.github.gozhuyinglong.reflection.Person"); 33 | } catch (ClassNotFoundException e) { 34 | // 当找不到指定类时,会抛出此异常 35 | e.printStackTrace(); 36 | } 37 | System.out.println("03 - " + clazz3); 38 | 39 | // 4.通过类加载器获取 40 | ClassLoader classLoader = this.getClass().getClassLoader(); 41 | Class clazz4 = null; 42 | try { 43 | clazz4 = classLoader.loadClass("io.github.gozhuyinglong.reflection.Person"); 44 | } catch (ClassNotFoundException e) { 45 | // 当找不到指定类时,会抛出此异常 46 | e.printStackTrace(); 47 | } 48 | System.out.println("04 - " + clazz4); 49 | 50 | // hashCode相等,说明这四种方式获取的是同一个实例 51 | System.out.println("05 - " + clazz1.hashCode()); 52 | System.out.println("06 - " + clazz2.hashCode()); 53 | System.out.println("07 - " + clazz3.hashCode()); 54 | System.out.println("08 - " + clazz4.hashCode()); 55 | 56 | } 57 | 58 | /** 59 | * 一些特殊的接口、类、关键字 60 | */ 61 | @Test 62 | public void testClassOther() { 63 | 64 | // 枚举是一种类 65 | Class clazz1 = PersonEnum.class; 66 | System.out.println("01 - " + clazz1); 67 | 68 | // 注解是一种接口 69 | Class clazz2 = PersonAnnotation.class; 70 | System.out.println("02 - " + clazz2); 71 | 72 | // 数组也属于一个反应 Class 实例的类 73 | Person[] personArray3 = new Person[1]; 74 | Class clazz3 = personArray3.getClass(); 75 | System.out.println("03 - " + clazz3); 76 | 77 | // 具有相同元素类型和维数的数组,也具有相同的 Class 实例 78 | Person[] personArray4 = new Person[4]; 79 | Class clazz4 = personArray4.getClass(); 80 | 81 | Person[][] personArray5 = new Person[1][]; 82 | Class clazz5 = personArray5.getClass(); 83 | 84 | // 两个一维数组的 hashCode 相等,说明是同一实例 85 | System.out.println("04 - " + clazz3.hashCode()); 86 | System.out.println("05 - " + clazz4.hashCode()); 87 | // 一维数组与二维数组的 hashCode 不相等,说明不是同一实例 88 | System.out.println("06 - " + clazz5.hashCode()); 89 | 90 | // 原始 Java 类型和关键字 void 也表示为 Class 实例 91 | Class clazz6 = int.class; 92 | System.out.println("07 - " + clazz6); 93 | 94 | Class clazz7 = double.class; 95 | System.out.println("08 - " + clazz7); 96 | 97 | Class clazz8 = void.class; 98 | System.out.println("09 - " + clazz8); 99 | 100 | } 101 | 102 | /** 103 | * 类 104 | * 105 | * @throws Exception 106 | */ 107 | @Test 108 | public void testClass() throws Exception { 109 | Class clazz = Class.forName("io.github.gozhuyinglong.reflection.Person"); 110 | 111 | // 获取该类所在包路径 112 | Package aPackage = clazz.getPackage(); 113 | System.out.println("01 - " + aPackage); 114 | 115 | // 获取该类上所有注解 116 | Annotation[] declaredAnnotations = clazz.getDeclaredAnnotations(); 117 | for (Annotation temp : declaredAnnotations) { 118 | System.out.println("02 - " + temp); 119 | } 120 | 121 | // 获取类上的修饰符 122 | int modifiers = clazz.getModifiers(); 123 | String modifier = Modifier.toString(modifiers); 124 | System.out.println("03 - " + modifier); 125 | 126 | // 获取类名称 127 | String name = clazz.getName(); 128 | System.out.println("04 - " + name); 129 | // 获取简单类名 130 | String simpleName = clazz.getSimpleName(); 131 | System.out.println("05 - " + simpleName); 132 | 133 | // 获取直属超类 134 | Type genericSuperclass = clazz.getGenericSuperclass(); 135 | System.out.println("06 - " + genericSuperclass); 136 | 137 | // 获取直属实现的接口 138 | Type[] genericInterfaces = clazz.getGenericInterfaces(); 139 | for (Type temp : genericInterfaces) { 140 | System.out.println("07 - " + temp); 141 | } 142 | 143 | } 144 | 145 | /** 146 | * 构造函数 147 | * 148 | * @throws Exception 149 | */ 150 | @Test 151 | public void testConstructor() throws Exception { 152 | Class clazz = Class.forName("io.github.gozhuyinglong.reflection.Person"); 153 | 154 | // 获取一个声明为 public 构造函数实例 155 | Constructor constructor1 = clazz.getConstructor(String.class, int.class, PersonEnum.class); 156 | System.out.println("01 - " + constructor1); 157 | 158 | // 获取所有声明为 public 构造函数实例 159 | Constructor[] constructorArray1 = clazz.getConstructors(); 160 | for (Constructor constructor : constructorArray1) { 161 | System.out.println("02 - " + constructor); 162 | } 163 | 164 | // 获取一个声明的构造函数实例 165 | Constructor constructor2 = clazz.getDeclaredConstructor(String.class); 166 | System.out.println("03 - " + constructor2); 167 | 168 | // 获取所有声明的构造函数实例 169 | Constructor[] constructorArray2 = clazz.getDeclaredConstructors(); 170 | for (Constructor constructor : constructorArray2) { 171 | System.out.println("04 - " + constructor); 172 | } 173 | 174 | // 根据构造函数创建一个实例 175 | Object o1 = constructor1.newInstance("杨过", 25, PersonEnum.MAN); 176 | System.out.println("05 - " + o1); 177 | 178 | // 将构造函数的可访问标志设为 true 后,可以通过私有构造函数创建实例 179 | constructor2.setAccessible(true); 180 | Object o2 = constructor2.newInstance("小龙女"); 181 | System.out.println("06 - " + o2); 182 | 183 | // 获取该构造函数上的所有注解 184 | Annotation[] annotations = constructor1.getDeclaredAnnotations(); 185 | for (Annotation annotation : annotations) { 186 | System.out.println("07 - " + annotation); 187 | } 188 | 189 | // 获取该构造函数上的所有参数类型 190 | Type[] genericParameterTypes = constructor1.getGenericParameterTypes(); 191 | for (Type genericParameterType : genericParameterTypes) { 192 | System.out.println("08 - " + genericParameterType); 193 | } 194 | 195 | } 196 | 197 | /** 198 | * 属性 199 | * 200 | * @throws Exception 201 | */ 202 | @Test 203 | public void testField() throws Exception { 204 | 205 | Class clazz = Class.forName("io.github.gozhuyinglong.reflection.Person"); 206 | 207 | // 获取一个该类或父类中声明为 public 的属性 208 | Field field1 = clazz.getField("hobby"); 209 | System.out.println("01 - " + field1); 210 | 211 | // 获取该类及父类中所有声明为 public 的属性 212 | Field[] fieldArray1 = clazz.getFields(); 213 | for (Field field : fieldArray1) { 214 | System.out.println("02 - " + field); 215 | } 216 | 217 | // 获取一个该类中声明的属性 218 | Field field2 = clazz.getDeclaredField("name"); 219 | System.out.println("03 - " + field2); 220 | 221 | // 获取该类中所有声明的属性 222 | Field[] fieldArray2 = clazz.getDeclaredFields(); 223 | for (Field field : fieldArray2) { 224 | System.out.println("04 - " + field); 225 | } 226 | 227 | // 获取该属性上的所有注解 228 | Annotation[] declaredAnnotations = field2.getDeclaredAnnotations(); 229 | for (Annotation declaredAnnotation : declaredAnnotations) { 230 | System.out.println("05 - " + declaredAnnotation); 231 | } 232 | 233 | // 获取修饰符 234 | String modifier = Modifier.toString(field2.getModifiers()); 235 | System.out.println("06 - " + modifier); 236 | 237 | // 获取属性类型,返回类对象 238 | Class type = field2.getType(); 239 | System.out.println("07 - " + type); 240 | // 获取属性类型,返回Type对象 241 | Type genericType = field2.getGenericType(); 242 | System.out.println("08 - " + genericType); 243 | 244 | // 获取属性名称 245 | String name = field2.getName(); 246 | System.out.println("09 - " + name); 247 | 248 | } 249 | 250 | 251 | /** 252 | * 方法 253 | * 254 | * @throws Exception 255 | */ 256 | @Test 257 | public void testMethod() throws Exception { 258 | 259 | Class clazz = Class.forName("io.github.gozhuyinglong.reflection.Person"); 260 | 261 | // 获取一个该类及父类中声明为 public 的方法,需要指定方法的入参类型 262 | Method method = clazz.getMethod("setName", String.class); 263 | System.out.println("01 - " + method); 264 | 265 | // 获取该类及父类中所有声明为 public 的方法 266 | Method[] methods = clazz.getMethods(); 267 | for (Method temp : methods) { 268 | System.out.println("02 - " + temp); 269 | } 270 | 271 | // 获取一个在该类中声明的方法 272 | Method declaredMethod = clazz.getDeclaredMethod("display"); 273 | System.out.println("03 - " + declaredMethod); 274 | 275 | // 获取所有在该类中声明的方法 276 | Method[] declaredMethods = clazz.getDeclaredMethods(); 277 | for (Method temp : declaredMethods) { 278 | System.out.println("04 - " + temp); 279 | } 280 | 281 | // 获取该方法上的所有注解 282 | Annotation[] declaredAnnotations = method.getDeclaredAnnotations(); 283 | for (Annotation temp : declaredAnnotations) { 284 | System.out.println("05 - " + temp); 285 | } 286 | 287 | // 获取修饰符 288 | String modifier = Modifier.toString(method.getModifiers()); 289 | System.out.println("06 - " + modifier); 290 | 291 | // 获取返回值类型,返回类对象 292 | Class returnType = method.getReturnType(); 293 | System.out.println("07 - " + returnType); 294 | // 获取返回值类型,返回Type对象 295 | Type genericReturnType = method.getGenericReturnType(); 296 | System.out.println("08 - " + genericReturnType); 297 | 298 | // 获取方法名称 299 | String name = method.getName(); 300 | System.out.println("09 - " + name); 301 | 302 | // 获取所有入参 303 | Parameter[] parameters = method.getParameters(); 304 | for (Parameter temp : parameters) { 305 | System.out.println("10 - " + temp); 306 | } 307 | 308 | } 309 | 310 | /** 311 | * 访问修饰符,以下简称:修饰符 312 | * 313 | * @throws Exception 314 | */ 315 | @Test 316 | public void testModifier() throws Exception { 317 | Class clazz = Class.forName("io.github.gozhuyinglong.reflection.Person"); 318 | 319 | // 获取类的修饰符值 320 | int modifiers1 = clazz.getModifiers(); 321 | System.out.println("01 - " + modifiers1); 322 | 323 | // 获取属性的修饰符值 324 | int modifiers2 = clazz.getDeclaredField("name").getModifiers(); 325 | System.out.println("02 - " + modifiers2); 326 | 327 | // 获取构造函数的修饰符值 328 | int modifiers3 = clazz.getDeclaredConstructor(String.class).getModifiers(); 329 | System.out.println("03 - " + modifiers3); 330 | 331 | // 获取方法的修饰符值 332 | int modifiers4 = clazz.getDeclaredMethod("display").getModifiers(); 333 | System.out.println("04 - " + modifiers4); 334 | 335 | // 判断修饰符值是否 final 类型 336 | boolean isFinal = Modifier.isFinal(modifiers1); 337 | System.out.println("05 - " + isFinal); 338 | 339 | // 判断修饰符值是否 public 类型 340 | boolean isPublic = Modifier.isPublic(modifiers2); 341 | System.out.println("06 - " + isPublic); 342 | 343 | // 根据修饰符值,获取修饰符标志的字符串 344 | String modifier = Modifier.toString(modifiers1); 345 | System.out.println("07 - " + modifier); 346 | System.out.println("08 - " + Modifier.toString(modifiers2)); 347 | 348 | } 349 | 350 | /** 351 | * 参数 352 | * 353 | * @throws Exception 354 | */ 355 | @Test 356 | public void testParameter() throws Exception { 357 | Class clazz = Class.forName("io.github.gozhuyinglong.reflection.Person"); 358 | 359 | // 获取构造函数的参数 360 | Constructor constructor = clazz.getConstructor(String.class, int.class, PersonEnum.class); 361 | Parameter[] parameterArray1 = constructor.getParameters(); 362 | for (Parameter temp : parameterArray1) { 363 | System.out.println("01 - " + temp); 364 | } 365 | 366 | // 获取方法的参数 367 | Method method = clazz.getMethod("setName", String.class); 368 | Parameter[] parameterArray2 = method.getParameters(); 369 | for (Parameter temp : parameterArray2) { 370 | System.out.println("02 - " + temp); 371 | } 372 | 373 | Parameter parameter = parameterArray1[0]; 374 | // 获取参数上的注解 375 | Annotation[] annotationArray = parameter.getAnnotations(); 376 | for (Annotation temp : annotationArray) { 377 | System.out.println("02 - " + temp); 378 | } 379 | 380 | // 获取参数名称 381 | String name = parameter.getName(); 382 | System.out.println("03 - " + name); 383 | 384 | // 获取参数类型 385 | Type parameterizedType = parameter.getParameterizedType(); 386 | System.out.println("04 - " + parameterizedType); 387 | Class type = parameter.getType(); 388 | System.out.println("05 - " + type); 389 | 390 | } 391 | 392 | /** 393 | * 创建对象并执行方法 394 | * 395 | * @throws Exception 396 | */ 397 | @Test 398 | public void testInvoke() throws Exception { 399 | Class clazz = Class.forName("io.github.gozhuyinglong.reflection.Person"); 400 | 401 | // 通过Class类的newInstance创建一个实例。(该方法调用无参构造器) 402 | Object o1 = clazz.newInstance(); 403 | System.out.println("01 - " + o1.toString()); 404 | 405 | // 通过构造函数Constructor类创建一个实例 406 | Constructor constructor = clazz.getConstructor(String.class, int.class, PersonEnum.class); 407 | Object o2 = constructor.newInstance("杨过", 25, PersonEnum.MAN); 408 | System.out.println("02 - " + o2.toString()); 409 | 410 | // 先获取方法,再通过 invoke 方法来调用,第一个参数为实例,后面参数为方法的Parameter 411 | Method method = clazz.getMethod("setName", String.class); 412 | method.invoke(o1, "小龙女"); 413 | System.out.println("03 - " + o1.toString()); 414 | 415 | // 获取字段,因为 age 字段是私有的,所以将其设置为可访问(不设置会报异常)。并通过 set 方法来赋值 416 | Field field = clazz.getDeclaredField("age"); 417 | field.setAccessible(true); 418 | field.set(o1, 28); 419 | System.out.println("04 - " + o1.toString()); 420 | 421 | } 422 | 423 | 424 | } 425 | --------------------------------------------------------------------------------