
能力说明:
掌握封装、继承和多态设计Java类的方法,能够设计较复杂的Java类结构;能够使用泛型与集合的概念与方法,创建泛型类,使用ArrayList,TreeSet,TreeMap等对象掌握Java I/O原理从控制台读取和写入数据,能够使用BufferedReader,BufferedWriter文件创建输出、输入对象。
阿里云技能认证
详细说明引言其实我是一个纯血 Java 开发,强行被拽上前端的车。为什么呢? 因为老板说不管前端、还是中端亦或是后端,都可以学。 就像你说你学软件的,我看你修起来公司的电脑也是蛮六六的。 这里建议大家在公司不要说自己会修电脑或是装系统,不然后面有这些事情就想起你来了。 不过,前端学起来蛮有意思的,就是更新的太快了。新手村当年在创业型公司,要前后端都着手去做。那时候的前端还是 jsp,一通声明 JQuery、Bootstrap 等,就开始写了。写完好大一堆,甚至里面还放一些 Java 代码,这个是支持的。这些代码后面再也不想去看了。然后期间写了很长一段时间,基本上页面的元素都是通过 document 去操作。写了一年多以后,知道了一个东西叫做 AngularJs,可以双向绑定。很开心了,因为 jsp 那个东西只能获取键盘上输入了什么然后拼接进行赋值。然后在这个前端知识海洋里一直狗刨式游啊游,开始去学一些骚东西。组件像幻灯片一样的特效飞来飞去,图片轮播定时器等,现在基本忘完。所以现在的封装好的框架,用起来很机械式的操作,不知道会不会以后把程序员变成简单的配置工人。稍微入门再后来,我就开始学习 Vue 了。跟着教程视频去看了 Vue2.x 版本的,学习以后就开始用。基本上也写完整个模块。现在也是忘个七七八八。依稀记得created、mounted、watch等。没有系统的去学习前端是因为,项目紧,任务重。不得不学一点东西,能看懂这些组件是干嘛的,就用上开始往代码里灌屎了。截止目前,2021 年 8 月 15 日,一直在用 Vue3.x 的版本,也用上了Typescript。现在对待学习很浮躁,学一会总想打开 QQ 或者微信水一会,找人唠一会。这也直接导致,我又一次没有系统的去学习前端的知识。但是我个人的建议是,去看看企业招聘前端需要的技能点去学习,而不是去看一下大佬发的学习路线。大佬们发的学习路线基本上都是从 JQuery 开始的,你时间不够的。有点基础还是建议拿项目直接上手撸,例如撸一个增删改查带业务的软件,用上现成的框架。现有框架:Ant Design of VueElementPlus现有框架在 GitHub 上开源的模板: element-plus-adminvue3-antd-admin类似这种开源项目模板还有很多,直接在 Github 的搜索中输入你想用的前端框架就能搜索到目前在用的模板vue-vben-admin示例图片: 比着照葫芦画瓢基本也写完一个系统,如下: 总结建议直接上手撸项目遇到组件或者模块不会的,百度或者 Google 下。简单学一下组件,不要浪费太多时间。组件之间关联很多,简单看一下就好,还是不要浪费太多时间。因为我们能高度集中精力学习的时间太少了。我没有去系统学习过,因此在系统学习的道路上,没有很好的建议。系统学习的时候,一定要打好笔记,因为真的真的太容易忘了。你这周学完了 promise,下周学完 emit,下下周再学其他的而不复习,等于没学。
介绍Java 中继承是一种机制,其中一个对象获取父对象的所有属性和行为。 它是 OOP(面向对象的编程系统) 的重要组成部分。 Java 继承的思想是,您可以 创建 基于现有类构建的新类。 从现有类继承时,可以 复用 父类的 方法 和 字段 。 此外,您还可以在继承后的类中添加新的方法和字段为什么需要继承试想一种情况: 有一个 Aminal (动物),它的定义如下:属性: 体重、身高、年龄 方法: 移动ok,当我们这个类定义好之后,现在再来定义一个 Dog(狗)的类别:属性: 体重、身高、年龄 、毛色 方法: 移动、吃、睡、吠叫 现在我们发现狗类和动物类中定义的属性有很多重复。 在现实中,狗是一种动物,应该拥有动物的属性及方法,然后再加上狗专属的属性和方法。 以这个例子来说,我们可以把动物当成 父类别(或称超类别super class) ,狗 继承 动物,狗是 子类别 subclass 。 子类别会拥有父类别的所有属性、方法,再加上自己定义的属性及方法,所以可以说子类别是父类别的延伸(extend)。 (tips:这句话超重要!多看个几十遍,想一想) 总结两点:实现方法重写 提高代码的复用性类别图这是 UML (统一建模语言,Unified Modeling Language) 的类别图,常用来描述类别之间的关系。 实心箭头表示继承关系,由子类别指向父类别。 图中读作 Dog 继承 Animal 。另外一种常见的说法是: Dog is a Animal. 继承的概念用 is a 来表述。反过来说 Animal is a Dog.是不成立的,利用 is a 可以帮助思考。继承中使用的术语Class:类是具有共同属性的一组对象。它是从中创建对象的模板或蓝图。Sub Class / Child Class:子类是继承其他类的类。也称为派生类,扩展类或子类。Super Class/ Parent Class:父类是子类从中继承要素的类。也称为基类或父类。Reusability:顾名思义,可重用性是一种机制,可在创建新类时方便您重用现有类的字段和方法。您可以使用在现有类中已经定义的相同字段和方法。Java继承的语法class Child extends Parent { //methods and fields } extends 关键字表示正在新建从现有类派生的新类,extends 的意思是扩展。 在 Java 术语中,被继承的类称为 父类 或 超类 ,而新类称为 子级 或 子类 。Java继承示例参照 类别图 :class Animal { int height; int weight; int age; void move() { System.out.println("移动 -> 移动"); } } class Dog extends Animal { String hairColor = "金黄"; void eat() { } void sleep() { } void bark() { } public static void main(String[] args) { Dog dog = new Dog(); System.out.print("狗开始移动:"); dog.move(); System.out.println("狗的颜色:"+ dog.hairColor); } }输出:狗开始移动:移动 -> 移动 狗的颜色:金黄.Java中的继承类型在类的基础上,Java 中可以有三种继承类型:单继承多级继承阶级式继承在 Java 编程中,仅通过 接口 支持 多重继承 和 混合继承 ,之后我们将学习接口。单继承 class Person { void eat() { System.out.println("吃饭..."); } } class Male extends Person { void speak() { System.out.println("中国话..."); } }多级继承class Person { void eat() { System.out.println("吃饭..."); } } class Male extends Person { void speak() { System.out.println("雄厚声..."); } } class BabyBoy extends Male { @Override void speak() { System.out.println("嚎嚎大哭..."); } }阶级式继承class Person { void eat() { System.out.println("吃饭..."); } } class Male extends Person { void speak() { System.out.println("雄厚声..."); } } class Female extends Person { void speak() { System.out.println("细细声..."); } }为什么 Java 不支持混合继承为了降低复杂性并简化语言,java 不支持多重继承。 考虑一个场景,其中 A,B 和 C 是三个类别。 C 类继承 A和B 类。 如果 A 和 B 类具有相同的方法,并且您从子类对象调用它,则将有歧义来调用 A 或 B 类的方法。 由于编译时错误比运行时错误要好,因此如果您继承 2 个类,则 Java 会呈现编译时错误。 因此,无论您使用的是相同的方法还是不同的方法,都会出现编译时错误。class A{ void msg(){System.out.println("Hello");} } class B{ void msg(){System.out.println("Welcome");} } class C extends A,B{ //假设可以运行 public static void main(String args[]){ C obj=new C(); obj.msg();// 不知道将要调用哪个msg()方法 } } 万物之父 Object如图:我们知道 Java 是面向对象的语言,而每个类都继承 Object。 定义类的时候,如果没有使用关键字 exyends ,Java 会自行继承 extends Object :class Animal{ // code } // 等价于下面代码中,Java会自动帮你extends Object class Animal extends Object{ // code }关键字 this、superthis指到自己类别的成员。class Human{ String name; int age; Human(String str){ this.name = str; } String getName(){ return this.name; } }上述程式中, this.name 意思是 自己这个类别的成员name ,当然在这个情况 不写也无所谓 ,但继承关系越 复杂 的情况下这样写法可以大大增加代码的 可读性 。调用构造方法 this(.); 如果写了很多构造提供多元的建构物件方式,构造之间彼此可以互相调用class Human{ String name; int age; static int totalCount = 0; Human(){ name = "untitled"; age = -1; totalCount++; } Human(String str){ this(); this.name = str; } Human(String str,int a){ this(str); this.age = a; } void printInfo(){ System.out.println(name+" 年齡:"+age+" 总人数:"+totalCount); } }上述程式中, this() 表示调用无参构造方法, this(String) 表示调用带有一个字串参数的构造方法,以此类推。 这样写的好处是,各构造之间有功能扩充的效果,已经写好的程式可以被充分的再利用,要修改某个环节也比较不会出错。 tips: this(.) 只能放在方法体内第一行!!!Human(String str){ // 编译错误,要把方法体内两行位置互换 this.name = str; this(); // 要等构造初始化完成,才能做最后的决定 }好,那用定义好的构造来测试一下程序:class TestHuman { public static void main(String[] args) { Human h1 = new Human(); h1.printInfo(); Human h2 = new Human("铅华"); h2.printInfo(); Human h3 = new Human("小花", 18); h3.printInfo(); } }执行结果:untitled 年齡:-1 目前總人數:1 铅华 年齡:-1 目前總人數:2 小花 年齡:18 目前總人數:3super指到父类别,使用方法跟 this 相似,一样要放到第一行。 使用代码测试一下:class Parent { int money = 100; public Parent(int money) { this.money -= money; } static class Child extends Parent { public Child(int money) { super(money); System.out.println("儿子花了爸爸" + money + "元钱后剩余" + this.money); } } public static void main(String[] args) { new Child(10); } }执行结果:儿子花了爸爸10元钱后剩余90层层初始化举个例子: class A{ A(){ System.out.println("A的构造...."); } } class B extends A{ B(){ System.out.println("B的构造...."); } } class C extends B{ C(){ System.out.println("C的构造...."); } }新建一个 C 对象试一试:class Test { public static void main(String[] args) { C c = new C(); } }执行结果:A的构造.... B的构造.... C的构造....示意图: 我们要建构的是 C ,而 C 是 B 的延伸,所以要先有 B ,而 B 是 A 的延伸,所以要先有 A ,而 A 是 Object 的延伸,所以要先有 Object ,于是就从最顶端的父类别一直建构下来。 好,现在我知道需要从父类别初始化下来,但构造呢?一个类别可以定义无数个构造,他怎么知道我要用哪个构造方法来构造我的对象?到底是以什么机制来构造父类的?嗯,回想一下,当初在定义类的时候,如果没有定义任何构造方法,Java 会帮你定义一个不带参数不做任何事的构造方法,现在同样的老招又来一次!只要你的构造方法中 没有调用其他构造方法 ,就会在 第一行 偷偷帮你加上去一个 super(); 有多偷偷呢?你连看都看不到! !但他就是存在于最后的程序代码中。 以上的程式来说,就像这样:class A{ A(){ super(); // 这行不写的话,Java会帮你加上,但你看不到 System.out.println("这里是A的构造方法"); } } class B extends A{ B(){ super(); // 这行不写的话,Java会帮你加上,但你看不到 System.out.println("这里是B的构造方法"); } } class C extends B{ C(){ super(); // 这行不写的话,Java会帮你加上,但你看不到 System.out.println("这里是C的构造方法"); } }好的,现在知道他会自动帮我调用 super(); 来建构父类别,但是如果我不想用这个· 无参构造 呢?我辛苦设计那么多 构造方法 ,他只会帮我调用 无参构造 ,太惨了吧!嗯嗯,没错就是这么惨,所以如果要调用 有参数的super(.); 你就要自己写!观察下述代码,想想执行结果:class ClassA { ClassA() { System.out.println("这里是A的构造方法..."); } } class ClassB extends ClassA { ClassB() { System.out.println("这里是B的构造方法..."); } ClassB(String str) { this(); System.out.println("B:hello " + str); } } public class ClassC extends ClassB{ ClassC() { this("tina"); System.out.println("这里是C的构造方法..."); } ClassC(String str) { super(str); System.out.println("C: hello "+ str); } public static void main(String[] args) { new ClassC(); } }执行结果:这里是A的构造方法... 这里是B的构造方法... B:hello tina C: hello tina 这里是C的构造方法...如果跟你想的不一样,在重新看一下上面的描述再想想,哪里不懂可以查看原文问我。这里是重要的 继承理念 。存取修饰符 protected在存取修饰符的章节提过,现在刚好提到继承再拿出来讨论。protected 是个关键字,开放的最大权限为 不同包的子类别 可以存取。假设 Animal 与 Dog 位在不同 package ,先看 Animal 的代码: package A; public class Animal { public String name; // 4个属性刚好4种权限范围都做测试 protected int height; int weight; private int age; // ↓这个修饰子一定要public或protected,不然不同类别的Dog不能用他来构造对象 public Animal(String str,int h,int w,int a){ this.name = str; this.height = h; this.weight = w; this.age = a; } }再看 Dog 的代码: package B; import A.Aminal; public class Dog extends Animal{ String color; public Dog(String str,int h,int w,int a,String c){ super(str,h,w,a); this.color = c; } public void printInfo(){ System.out.println(name); // OK, public 不同包也可以存取 System.out.println(height); // OK, protected 允许不同包的子类别存取 System.out.println(weight); // 编译错误,预设只有同包可以存取 System.out.println(age); // 编译错误,private 只有自身类中能存取 System.out.println(color); // OK, 当前类成员当然ok } }重写的存取修饰符限制上面的例子有提到过重写(override),这边再详细讨论一下,以及一些限制。在继承中关系,父类别定义了一些方法,子类别觉得不适用的话可以 覆盖 掉父类别的方法,然后 重写 属于自己的方法。举个例子:class A{ void printInfo(){ System.out.println("hello, I am A."); } } class B extends A{ void printInfo(){ System.out.println("hello, I am B."); } } class C extends A{ }测试代码:class Test { public static void main(String[] args) { B b = new B(); b.printInfo(); C c = new C(); c.printInfo(); } }执行结果:hello, I am B. hello, I am A.上述程式中, B 与 C 都是继承 A ,表示拥有了 A 所有的成员,但 B 重写了 printInfo() 方法,而 C 没有。所以在调用的时候, 对象B 会使用 B 类别重写的方法,而 对象C 因为 C 类没有自己定义重写,所以会使用到父类 A 所定义的 printInfo() 。好,那来谈谈 重写 的 限制 。要重写父类方法必须满足几个条件:父类方法不能用 final 修饰。子类重写的方法名称、回传型态、参数个数顺序需相同。子类重写的方法,其修饰符权限不能小于父类方法。第一点,用final修饰的方法无法被重写。这是关键字 final 修饰方法的特性,详细内容于后面讨论。class A{ // (↓关键字 final) public final void printInfo(){ System.out.println("hello, this is A."); } } class B extends A{ // 编译错误 ↓ 利用final修飾的方法不能被重写。 public void printInfo(){ System.out.println("hello, this is B;"); } }在 A类 的 printInfo() 方法利用 关键字 final 修饰,所以任何继承他的子类别都不能重写这个方法,否则会产生编译错误: Cannot override the final method from A .第二点,方法名称、回传型态、参数个数必须相同。嗯,如果不一样的话,就是自己再定义一个新方法了阿! !跟重写有什么关系 XDclass A{ public void printInfo(){ System.out.println("hello, this is A."); } } class B extends A{ public void printInfo2(){ System.out.println("hello Tina, nice to meet you <3"); } }恩,就是多定义一个方法,没什么好说的,这根本不是重写。 第三点,子类方法修饰符权限不能小于父类方法。简单来说,如果父类说这个方法是对 全世界公开(public) 的方法,你要重写就不能 占为己有(private) 。tips: 存取修饰符的开放权限从大到小: public -> protected -> (no modifier) -> private 如果父类说此方法是 protected ,那子类重写时的修饰符必须是 public 或 protected 。如果父类说此方法是 private ,那子类重写时的修饰符必须是 public 或 protected 或 (no modifier) 或 private 。关键是权限的开放范围不得小于重写对象。class A{ // 注意修饰符是(no modifier) void printInfo(){ System.out.println("hello, this is A."); } } class B extends A{ // ↓ 编译错误,子类重写方法修饰符权限小于父类方法 private void printInfo(){ System.out.println("hello, this is B."); } }在 A类 中的 printInfo() 方法修饰子是 (no modifier) ,依据重写的开放权限规则, B类 继承了 A类 想重写 printInfo() ,重写的开放权限必须为 public 或 protected 或 ( no modifier) ,重点就是不能小于重写对象,否则会发生编译错误: Cannot reduce the visibility of the inherited method from A .
Java 中 String 与 StringBuffer 和 StringBuilder 的区别介绍String 是 Java 中很常用的类之一,同时,字符串是 Java 面试中最重要的话题之一。 StringBuffer 和 StringBuilder 类提供了操作字符串的方法。 我们将研究 StringBuffer 和 StringBuilder 之间的区别。 StringBuffer 与 StringBuilder 的区别是 Java 中很常见的面试题。在控制台上打印程序的内容,可以使用 String。此博客介绍 String 类的主要功能,然后我们将比较 StringBuffer 和 StringBuilder 类。String注意一个常见的错误,不要记错了。因为 String 是 final 修饰的,无法被继承。所以 String 不是 Java 的基本数据类型。 字符串在 Java 中是不可变的,因此适合在多线程环境下使用。 当我们使用双引号创建一个字符串时,如下,JVM 首先在字符串池中寻找具有相同值的字符串。String str1 = "ABC";如果找到了,它将返回字符串池中的字符串对象的引用。否则,它会在字符串池中创建字符串对象并返回引用。JVM 通过在不同的线程中使用相同的字符串,节省了大量的内存。 如果使用 new 运算符创建字符串,则会在堆中创建它。运算符 + 是为 String 重载的,我们可以用它来串联两个字符串。尽管在内部它使用 StringBuilder 来执行这个动作。 两个字符串只有在它们具有相同字符串的时候才相等,equals() 方法区分大小写。如果您正在寻找不区分大小写的检查,您应该使用 equalsIgnoreCase() 方法。拼接字符串由于 String 在 Java 中是不可变的,因此每当我们执行字符串拼接操作时,它都会生成一个新的 String 并丢弃旧的 String 以进行垃圾收集。 这些重复的操作会在堆中产生大量垃圾冗余。所以 Java 提供了 StringBuffer 和 StringBuilder 类,应该用于字符串操作。 StringBuffer 和 StringBuilder 是 Java 中的可变对象。 它们为字符串操作提供了 append、insert、delete 和 substring 方法。StringBufferStringBuilder线程安全非线程安全同步非同步始于 Java 1.0始于 Java 1.5慢快在 Java 1.4 之前,StringBuffer 是字符串操作的唯一选择。但是,它的一个缺点是所有公共方法都是同步的。 StringBuffer 提供线程安全性,但以性能为代价。 在大多数情况下,我们不会在多线程环境中使用 String。所以 Java 1.5 引入了一个新类 StringBuilder,除了线程安全和同步之外,它与 StringBuffer 类似。StringBuffer 有一些额外的方法,例如 substring, length, capacity, trimToSize 等。但是,这些不是必需的,因为 String 中也有所有这些。这就是为什么这些方法从未在 StringBuilder 类中实现的原因。StringBuffer 是在 Java 1.0 中引入的,而 StringBuilder 类是在查看 StringBuffer 的缺点后在 Java 1.5 中引入的。假设在单线程环境中或无关线程安全,要使用 StringBuilder。反之,使用 StringBuffer 进行线程安全的操作。总结String 是不可变的,而 StringBuffer 和 StringBuilder 是可变类。StringBuffer 是线程安全和同步的,而 StringBuilder 不是。这就是 StringBuilder 比 StringBuffer 快的原因。字符串连接运算符 (+) 在内部使用 StringBuilder 类。对于非多线程环境中的字符串操作,我们应该使用 StringBuilder 否则使用 StringBuffer 类。
介绍多态就是指一个类实例的相同方法在不同情形有不同表现形式,在面向对象设计的概念中,对于父类提供的方法调用,子类可以有自己特有的行为。例子class Animal { void move() { System.out.println("move...move..."); } } class Dog extends Animal { void move() { System.out.println("跑...跑..."); } } class Bird extends Animal { void move() { System.out.println("飞...飞..."); } } class 源氏 extends Animal { void move() { System.out.println("爬...爬..."); } }声明一个 Animal 类,然后创建 Dog 、 Brid 、 Fish 类来继承 Animal 类还记得继承关系可以用 IS-A 来表述吗?我可以说 Dog is a Animal 或 Bird is a Animal 或 Fish is a Animal 这是 OK 且符合逻辑的。好,那现在我有 4 只动物,是什么动物不管,我就把它们都当成动物就好。我知道动物里面有一个 move() 的方法,我让每只动物都使用这个 move() 的方法。程序如下:class Test { public static void main(String[] args) { Animal a =new Animal(); Animal b =new Dog(); Animal c =new Bird(); Animal d =new Fish(); a.move(); b.move(); c.move(); d.move(); } }变量a 看起来 OK,声明是 Animal 且也用 Animal 的 构造函数 去初始化。变量b 看起来就怪怪的,用 Dog() 来初始化的话不是应该声明成 Dog 型态吗?是的,那样写也 OK,我们知道依据继承关系, Dog is a Animal 所以我这样写也并非违反逻辑。利用 Dog() 构造方法生成的对象,理当拥有 Dog 类定义的所有成员,但不管,现在我们只把这个物件当作 Animal 来看。变数c ,不管你怎么构造你的对象,我都会用 Animal 的角度去看你。变数d :本体是 Fish ,但有人把我当成动物。 (变数 b,c,d 是差不多情况)好,现在我不管它们 4 个的 本质 是什么,我只知道我把它们都当做 Animal 来看,而我知道 Animal 里面有定义一个方法叫做 move() ,所以把 4 只 Animal 的 move() 都调用看看。 执行结果:move...move... 跑...跑... 飞...飞... 游...游...只要可以视为 Animal 的对象,就可以使用这个方法而不会出错。这样的设计方式可以 降低方法定义对类的依赖 ,使用一个制定好的介面,利用该介面来操作不同的对象,增加代码的复用性,架构设计上也比较好类型转换只能向上转型,例如可以从子类转换为父类,因为子类中存在的方法父类中也存在,当然除了 private 修饰的,但是 子类可以扩展 自己的属性与方法,所以不能把父类强转为子类向上转型Dog d = new Dog(); Animal a1 = d; // Java帮你作了型态转换,但你看不到,等价于下行 Animal a2 = (Animal)d; // 也可以自己写向下转型Animal ani = new Animal(); Dog d = (Dog)ani; // 父类强转为子类执行结果:Exception in thread "main" java.lang.ClassCastException: Animal cannot be cast to Dogtips:编译会过,执行会错,切记!
Java装箱与自动拆箱介绍Java 编译器在基本数据类型和其对应的对象封装类型之间进行自动转换.例如将int转成Integer,将char转成包装类Character等等.其他基本数据类型的转换如下: Java 中 8 种基本数据类型基本数据类型类型大小包装类取值范围取值范围Probyte数值型8bitByte-2^7 ~ 2^7-1-128 ~ 127short短整形16bitShort-2^15 ~ 2^15-1-32768 ~ 32767int数值整型32bitInteger-2^31 ~ 2^31-1-2147483648 ~ 2147483647long数值长整形64bitLong-2^63 ~ 2^63-1-9223372036854775808 ~ 9223372036854775808float单精度数值型32bitFloat 3.4e-45 ~ 1.4e38double双精度数值型64bitDouble 4.9e-324 ~ 1.8e308char短整形16bitCharacter0~65535\u0000 ~ \uffffboolean布尔值8bit(争议)Boolean//反之,从包装类转换成基本数据类型叫拆箱.例如从Integer到int或者从包装类Long转换成基本数据类型long.简单的装箱示例Integer numA = 10; Character charA = 'a';从原始数据类型创建整数对象Integer b = new Integer(9);看一下下面的代码:List<Integer> li = new ArrayList<>(); for (int i = 1; i < 50; i += 2) li.add(Integer.valueOf(i));上面的代码将 Java 基本数据类型 int ,而不是对应的 Integer 对象添加到 list 集合中,代码为什么会编译通过呢? 因为它从 i 创建了一个 Integer 对象并将该对象添加到 li集合中. 因此,编译器在运行时将之前的代码转换为以下代码:List<Integer> li = new ArrayList<>(); for (int i = 1; i < 50; i += 2) li.add(Integer.valueOf(i));将基本数据类型值(例如 int)转换为相应包装类(Integer)的对象称为自动装箱。当基本数据类型在下面的使用情况的时候,会进行自动装箱:作为参数传递给需要相应包装类的对象的方法。赋值给相应包装类的变量.自动拆箱int a = 9; Integer b = new Integer(9); System.out.println(a==b); // 返回true数学运算 +, %, += 不适用于 Integer 对象。 Java 编译器编译代码时没有错误,因为它自动执行拆箱,在运行时调用 intValue 将 Integer 转换为 int.System.out.println(a==b.intValue());将包装类型 (Integer) 的对象转换为其对应的原始 (int) 值称为拆箱。当包装类使用情况如下的时候,会进行自动拆箱:作为参数传递给需要相应原始类型值的方法分析如下代码:public class TestTran { public static void main(String[] args) { unboxing(new Integer(9)); } public static void unboxing(int testId) { System.out.println(testId); } }可以看到参数需要的是基本数据类型.传过去一个 Integer 对象,编译器没有报错.因为作为参数传递给需要相应原始类型值的方法,Java 编译器做了自动拆箱的操作.赋值给相应原始类型的变量分析如下代码:Integer a = new Integer(9); int b =a;当把对应的包装类型 b 对象,赋值给给相应原始类型的变量的时候.Java 编译器就会做自动拆箱的操作.自动转换(拓展)从低范围转到高范围类型转换属于自动类型转换, 是 JVM 自动进行转换的. 如上图中,byte char short这三个是平级的 自动转型的示例代码如下:int num = 100; double numPro = num; System.out.println("转换前:" + num); System.out.println("转换后:" + numPro);代码输出如下:转换前:100 转换后:100.0面试题1.给出如下代码,请回答输出了什么public static void main(String[] args) { Integer numA = 100; Integer numB = 100; Integer numC = 128; Integer numD = 128; System.out.println(numA == numB); System.out.println(numC == numD); }想一下会输出什么,这样的面试题层出不穷,有没有什么好应对的方式 理理清楚自动装箱与拆箱,例如开篇的表格中有一个范围,Integer 的范围 Integer 把这个范围(-128 ~ 127)当成了一个缓存池,如果不在这个范围会使用 new 创建 所以现在的代码输出是:true false2. Java 1.5 的自动装箱拆箱机制是编译时执行还是虚拟机运行时执行 答:编译时执行. 3.给出如下代码,请回答输出了什么public static void main(String[] args) { Double numA = 10.0; Double numB = 10.0; System.out.println(numA == numB); System.out.println(numA.equals(numB)); System.out.println(numA == 10); }上面的结果为 false true true 分析: 为什么 System.out.println(numA == 10)也是true 我这里使用 IDEA 查看编译的字节码,如下:LINENUMBER 10 L6 GETSTATIC java/lang/System.out : Ljava/io/PrintStream; ALOAD 1 INVOKEVIRTUAL java/lang/Double.doubleValue ()D LDC 10.0 DCMPL IFNE L7 ICONST_1 GOTO L8因为做了拆箱操作,等同于System.out.println(numA.doubleValue() == 10);
引言在使用集合 ArrayList 的时候,经常使用add、remove等,其他的没用过,甚至没听说过的还有很多.现在在这个教程中,简单的了解一下,不要求全都记下.相当于在你脑袋里建一个索引,就是有些方法在用到的时候,不要去重复的造轮子而已. ArrayList 结构体如下包含构造方法总共是33个方法.开始以下方法排名不分先后ArrayList()可以使用new ArrayList() 创建一个 ArrayList 集合,如下:/** * 1 简单的ArrayList */ public static ArrayList getArrayList(){ ArrayList arrayList = new ArrayList(); arrayList.add("张三"); arrayList.add("里斯"); return arrayList; }一些编辑器中会报黄线或者淡黄背景提示,如下图 这个需要给 ArrayList 一个类型,例如 ArrayList<String> .ArrayList(Collection<? extends E> c)可以放入一个集合体来初始化 ArrayList,示例代码如下:HashSet<String> temp1 = new HashSet<>(); temp1.add("张三"); temp1.add("里斯"); ArrayList<String> arrayList2 = new ArrayList<>(temp1); arrayList2.forEach(System.out::println);ArrayList(int initialCapacity)构造一个具有指定初始容量的空列表,应用场景就是当你大概知道这个集合存储的数据量,直接定义好容量,避开集合自增空间浪费资源.ArrayList<String> arrayList3 = new ArrayList<>(1000);add() 与 add(int, E)add()方法是将括号内的值增加到集合末尾. add(int, E)是将数据插入的具体的下标上,下表从零开始.ArrayList<String> arrayList = new ArrayList<>(); arrayList.add("张三"); arrayList.add(0,"在天"); arrayList.add("里斯"); arrayList.forEach(System.out::println);addAll(Collection<? extends E> c)指集合中的所有元素追加到此列表的末尾;ArrayList<String> arrayList = new ArrayList<>(); arrayList.add("张三"); arrayList.add("李四"); arrayList.add("王二"); ArrayList<String> arrayList2 = new ArrayList<>(); arrayList2.add("麻子"); arrayList2.add("铁子"); arrayList.addAll(arrayList2); System.out.println(arrayList);输出:[张三, 李四, 王二, 麻子, 铁子]addAll(int index,Collection<? extends E> c)相当于是add(int index,E) 与 addAll(Collection<? extends E> c) 结合版 在指定的索引下标,依次追加指定集合中的所有元素.例如上一个例子,我想在张三后面就把麻子与铁子都插队进去,那么如下来实现一下.public static void testAddAllByIndex(){ ArrayList<String> arrayList = new ArrayList<>(); arrayList.add("张三"); arrayList.add("李四"); arrayList.add("王二"); ArrayList<String> arrayList2 = new ArrayList<>(); arrayList2.add("麻子"); arrayList2.add("铁子"); arrayList.addAll(1,arrayList2); System.out.println(arrayList); }输出:[张三, 麻子, 铁子, 李四, 王二]clear()看名字就应该清楚.从此列表中删除所有元素.此调用返回后,列表将为空,不是Null.public static void testClear() { ArrayList<String> arrayList = new ArrayList<>(); arrayList.add("张三"); arrayList.add("李四"); arrayList.add("王二"); System.out.println("执行 clear() 前,arrayList.size=" + arrayList.size()); arrayList.clear(); System.out.println("执行 clear() 后,arrayList.size=" + arrayList.size()); }输出:执行 clear() 前,arrayList.size=3 执行 clear() 后,arrayList.size=0clone()在说明这个之前我们先举一个栗子. 拷贝对象public static void testCloneTemp() { ArrayList<String> arrayList = new ArrayList<>(); arrayList.add("张三"); arrayList.add("李四"); arrayList.add("王二"); ArrayList<String> arrayList2 = arrayList; arrayList.add("混子"); arrayList2.add("王多鱼"); System.out.println(arrayList2); }输出:[张三, 李四, 王二, 混子, 王多鱼]我们其实想达到的效果是arrayList2 不要混子,只需要王多鱼.但是我们使了=导致了他们两个的物理地址指向了同一个,这个时候就体现到了clone的重要性.public static void testClone() { ArrayList<String> arrayList = new ArrayList<>(); arrayList.add("张三"); arrayList.add("李四"); arrayList.add("王二"); ArrayList<String> arrayList2 = (ArrayList<String>) arrayList.clone(); arrayList.add("混子"); arrayList2.add("王多鱼"); System.out.println(arrayList2); }输出:[张三, 李四, 王二, 王多鱼]这样他们就不会互相影响到.contains(Object o)列表包含元素o就返回true,否则就返回false;public static void testContains() { ArrayList<String> arrayList = new ArrayList<>(); arrayList.add("张三"); arrayList.add("李四"); arrayList.add("王二"); boolean existMazi = arrayList.contains("麻子"); boolean existZhangsan = arrayList.contains("张三"); System.out.printf("存在张三:%s,存在麻子:%s%n", existZhangsan, existMazi); }输出:存在张三:true,存在麻子:falseensureCapacity(int size)变更ArrayList的容量为size.public static void testChangeCapacity() throws NoSuchFieldException, IllegalAccessException { ArrayList<String> arrayList = new ArrayList<>(100); int sizeBefore = getCapacity(arrayList); arrayList.ensureCapacity(1000); int sizeAfter = getCapacity(arrayList); System.out.printf("更改前的size=%d,更改后的size=%d", sizeBefore, sizeAfter); }输出:更改前的size=100,更改后的size=1000 方法getCapacity为ArrayList获取容量大小的自定义方法,如下:public static int getCapacity(ArrayList<?> arrayList) throws NoSuchFieldException, IllegalAccessException { Class<ArrayList> arrayListClass = ArrayList.class; Field field = arrayListClass.getDeclaredField("elementData"); field.setAccessible(true); Object[] objects = (Object[]) field.get(arrayList); return objects.length; }forEach 方法遍历集合不要在使用forEach的同时使用remove和 add 方法, 不然会报异常. 至于为什么,这里就不拉开来讲了,因为我还没看懂,搜索一下后在评论给我讲讲. 使用蛮便捷的,就是上面的使用方法,只是不要调用新增与删除的方法就好.get(int index)根据下标获取对应下标的值.public static void testGet(){ ArrayList<String> arrayList = new ArrayList<>(); arrayList.add("张三"); arrayList.add("李四"); arrayList.add("王二"); for (int i = 0; i < arrayList.size(); i++) { String valueByIndex = arrayList.get(i); System.out.println(valueByIndex); } }输出:张三 李四 王二indexOf()根据值获取对应的下标.public static void testindexOf() { ArrayList<String> arrayList = new ArrayList<>(); arrayList.add("张三"); arrayList.add("李四"); System.out.printf("获取李四的下标:%d%n", arrayList.indexOf("张三")); System.out.printf("获取李四一的下标:%d%n", arrayList.indexOf("李四一")); }输出:获取李四的下标:0 获取李四一的下标:-1isEmpty()判断是否为空集合,空返回true,否则反之返回true.不能用于null.public static void testIsEmpty() { ArrayList<String> arrayList = new ArrayList<>(100); System.out.printf("获取是否为空集合:%s%n", arrayList.isEmpty()); }输出:获取是否为空集合:trueiterator()获取迭代器,使用迭代器遍历集合.只能使用一次,二次使用需要重新获取.例如下面代码我注释的地方,再去使用已经没有效果.public static void testIterator() { ArrayList<String> arrayList = new ArrayList<>(); arrayList.add("张三"); arrayList.add("李四"); Iterator<String> iterator = arrayList.iterator(); while (iterator.hasNext()){ String str = iterator.next(); System.out.println(str); if("张三".equals(str)){ iterator.remove(); } } // while (iterator.hasNext()) System.out.println(arrayList); }输出:张三 李四 [李四]lastIndexOf(Object o)返回指定对象在此集合中最后一次出现处的下标.如果找到对象,返回下标,找不到对象就返回-1.public static void testLastIndexOf() { ArrayList<String> arrayList = new ArrayList<>(); arrayList.add("张三"); arrayList.add("李四"); arrayList.add("张三"); int lastIndexZs = arrayList.lastIndexOf("张三");//应该返回2 int lastIndexLsy = arrayList.lastIndexOf("李四一"); System.out.printf("张三最后出现的下标为:%d,李四一最后出现的下标为:%d%n", lastIndexZs, lastIndexLsy); }输出:张三最后出现的下标为:2,李四一最后出现的下标为:-1listIterator()像是iterator的升级版,可以正逆向遍历.支持遍历过程中修改数据,例如set、remove、add.public static void testListIterator() { ArrayList<String> arrayList = new ArrayList<>(); arrayList.add("张三"); arrayList.add("李四"); ListIterator<String> listIterator = arrayList.listIterator(); String firstIndex = listIterator.next(); System.out.printf("开始删除:%s,arrayList:%s%n", firstIndex, arrayList); listIterator.remove(); System.out.printf("删除后,arrayList%s%n" , arrayList); listIterator.add("张三"); listIterator.next(); listIterator.set("李四替身"); System.out.printf("set后,arrayList%s%n" , arrayList); int prevIndex = listIterator.previousIndex(); System.out.printf("获取上一个元素的下标%d%n" , prevIndex); listIterator.previous(); listIterator.set("张三替身"); System.out.printf("set后,arrayList%s%n" , arrayList); }输出:开始删除:张三,arrayList:[张三, 李四] 删除后,arrayList[李四] set后,arrayList[张三, 李四替身] 获取上一个元素的下标1 set后,arrayList[张三, 张三替身]listIterator(int index)从指定下标开始遍历.public static void testListIteratorStartIndex() { ArrayList<String> arrayList = new ArrayList<>(); arrayList.add("张三"); arrayList.add("李四"); arrayList.add("王二"); // 因法外狂徒张三入狱,只取后面的值 ListIterator<String> iterator = arrayList.listIterator(1); while (iterator.hasNext()) { System.out.println(iterator.next()); } }输出:李四 王二remove(int index)通过下标移除对象,如果进入下标不存在,会出现IndexOutOfBoundsException异常.移除成功会返回对象.public static void testRemove() { ArrayList<String> arrayList = new ArrayList<>(); arrayList.add("张三"); arrayList.add("李四"); String removeStr = arrayList.remove(1); System.out.println(removeStr); }输出:李四remove(Object obj)移除对象,测试后只会移除第一个匹配的值.移除成功返回true,否之返回false.public static void testRemoveByObject() { ArrayList<String> arrayList = new ArrayList<>(); arrayList.add("张三"); arrayList.add("李四"); arrayList.add("张三"); arrayList.add(null); System.out.printf("arrayList.remove("张三")返回=%s%n",arrayList.remove("张三")); System.out.printf("arrayList=%s%n",arrayList); }输出:arrayList.remove("张三")返回=true arrayList=[李四, 张三, null]removeAll(Collection<?> c)移除传入集合中的对象.public static void testremoveAll() { ArrayList<String> arrayList = new ArrayList<>(); arrayList.add("张三"); arrayList.add("李四"); arrayList.add("张三"); arrayList.removeAll(new ArrayList<String>() {{ add("张三"); }}); System.out.printf("arrayList=%s%n", arrayList); }输出:arrayList=[李四]removeIf()删除满足特定条件的对象.public static void testRemoveIf() { ArrayList<String> arrayList = new ArrayList<>(); arrayList.add("张三"); arrayList.add("李四"); arrayList.add("张三"); System.out.printf("删除张三前,arrayList=%s%n", arrayList); arrayList.removeIf("张三"::equals); System.out.printf("删除张三后,arrayList=%s%n", arrayList); }输出:删除张三前,arrayList=[张三, 李四, 张三] 删除张三后,arrayList=[李四]replaceAll()替换元素,例如将集合内元素全转为大写,或者全部元素进行计算.public static void testReplaceAll() { ArrayList<String> arrayList = new ArrayList<>(); arrayList.add("张三"); arrayList.add("李四"); arrayList.add("王五"); arrayList.add("赵六"); arrayList.replaceAll(item -> "姓名:" + item); System.out.printf("arrayList=%s%n", arrayList); }输出:arrayList=[姓名:张三, 姓名:李四, 姓名:王五, 姓名:赵六]retainAll(Collection<?> c)取两个集合的并集,剔除不在两集合中同时存在的元素;public static void testRetainAll() { ArrayList<String> arrayList = new ArrayList<String>() {{ add("张三"); add("李四"); add("王五"); add("赵六"); }}; arrayList.retainAll(Arrays.asList("王五", "赵六")); System.out.printf("arrayList=%s%n", arrayList); }输出:arrayList=[王五, 赵六]set(int index, E element)根据下标替换或者插入对象. 示例,设置集合中下标为1的值为鲁班七号.public static void testSet() { ArrayList<String> arrayList = new ArrayList<String>() {{ add("张三"); add("李四"); add("王五"); add("赵六"); }}; System.out.printf("设置前,arrayList=%s%n", arrayList); arrayList.set(1,"鲁班七号"); System.out.printf("设置后,arrayList=%s%n", arrayList); }方法运行输出结果为:设置前,arrayList=[张三, 李四, 王五, 赵六] 设置后,arrayList=[张三, 鲁班七号, 王五, 赵六]size()返回集合中的数据条数.sort(Comparator<? super E> c)对集合内对象进行以指定方式排序.public static void testSort() { ArrayList<Integer> arrayList = new ArrayList<Integer>() {{ add(100); add(200); add(40); add(80); }}; System.out.printf("排序前,arrayList=%s%n", arrayList); arrayList.sort(Comparator.naturalOrder()); System.out.printf("自然顺序排列后,arrayList=%s%n", arrayList); arrayList.sort(Comparator.reverseOrder()); System.out.printf("倒序排列后,arrayList=%s%n", arrayList); }方法运行输出结果为:排序前,arrayList=[100, 200, 40, 80] 自然顺序排列后,arrayList=[40, 80, 100, 200] 倒序排列后,arrayList=[200, 100, 80, 40]spliterator()并行迭代器,就是把集合中的对象放到迭代器中. 然后,可以开启多个线程并行处理这些对象. 示例代码如下:public static void testSpliterator() { ArrayList<Integer> arrayList = new ArrayList<>(Arrays.asList(1, 2, 3, 4, 5, 6)); Spliterator<Integer> sItr = arrayList.spliterator(); // 遍历后迭代器中的值也会消失 // sItr.forEachRemaining(d -> System.out.print(d)); //123456 new Thread(() -> { for (int i = 0; i < 4; i++) { sItr.tryAdvance(d -> System.out.printf("线程:%s,抢到了:%d%n", Thread.currentThread().getName(), d)); } }).start(); new Thread(() -> { for (int i = 0; i < 4; i++) { sItr.tryAdvance(d -> System.out.printf("线程:%s,抢到了:%d%n", Thread.currentThread().getName(), d)); } }).start(); }方法运行输出结果为:线程:Thread-0,抢到了:1 线程:Thread-0,抢到了:3 线程:Thread-0,抢到了:4 线程:Thread-0,抢到了:5 线程:Thread-1,抢到了:2 线程:Thread-1,抢到了:6subList(int formIndex,int toIndex)截取集合的一部分并返回一个List集合. 图来自菜鸟教程 示例代码如下:public static void testSubList() { ArrayList<String> arrayList = new ArrayList<>(Arrays.asList("p", "r", "o", "g", "r", "a", "m")); List<String> subList1 = arrayList.subList(0, 7); System.out.printf("arrayList.subList(0,7) result:%s%n", subList1); List<String> subList2 = arrayList.subList(3, 7); System.out.printf("arrayList.subList(3,7) result:%s%n", subList2); List<String> subList3 = arrayList.subList(3, 6); System.out.printf("arrayList.subList(3,6) result:%s%n", subList3); }方法运行输出结果为:arrayList.subList(0,7) result:[p, r, o, g, r, a, m] arrayList.subList(3,7) result:[g, r, a, m] arrayList.subList(3,6) result:[g, r, a]toArray() && toArray(T[] a)返回一个当前集合顺序排列并且包含 ArrayList 中所有元素的数组. 示例代码如下:public static void testToArray() { // 1. toArray() ArrayList<Integer> arrayList = new ArrayList<>(Arrays.asList(10, 20, 3, 41, 15, 26)); Object[] num = arrayList.toArray(); System.out.println(Arrays.toString(num)); // 2. toArray(T[] a) Integer[] arr = new Integer[arrayList.size()]; arr = arrayList.toArray(arr); System.out.println(Arrays.toString(arr)); }方法运行输出结果为:[10, 20, 3, 41, 15, 26] [10, 20, 3, 41, 15, 26]trimToSize()去除ArrayList多余的容量. 示例代码如下:public static void testTrimToSize() throws NoSuchFieldException, IllegalAccessException { ArrayList<Integer> arrayList = new ArrayList<>(100); arrayList.add(11); arrayList.add(12); arrayList.add(13); System.out.printf("trimToSize之前ArrayList容量大小:%d%n", getCapacity(arrayList)); arrayList.trimToSize(); System.out.printf("trimToSize之后ArrayList容量大小:%d%n", getCapacity(arrayList)); }方法运行输出结果为:trimToSize之前ArrayList容量大小:100 trimToSize之后ArrayList容量大小:3写完辽不用死记硬背,收藏起来当成字典查一查. 上面代码的地址 https://github.com/cuifuan/house/blob/master/src/test/java/com/home/test/TestArrayListFun.java 告辞!
引言面试,其实是作为 Java 学习路上的一个总结。日常是在学习或是在摸鱼,一试便知。也是我们在学习路上相对来说最重要的一环,决定我们的薪资与高度。许多人害怕面试,我也怕诶,因为作为程序员整日面对电脑的情况,直接导致我们丧失一部分与人交流的能力。进阶这个时候我们要去想一个法子,让我们在面试的时候不至于那么狼狈。这个时候就是要去开始尽力去学习,可是你又说到你996,偶尔做一个007,没有那么多时间去学习进阶。这个时候,我们就在日常的工作中穿插基础的学习,例如你用 ArrayList 的时候,按住 Command 点一下进去看一下有哪些自带方法,然后用到你的工作当中去。在进阶大手子之前不要写很多花里胡哨的代码,尽量求稳,用最坚实易懂的代码先把功能做好。之后,你每一次去抽取代码的过程都是一个学习的过程。做到工作与学习两不耽误,可谓是一石二鸟。日常的代码异常,像那种超过半小时才搞定的 BUG 一定要记录下来,哪怕是错一个标点符号。收集起来,你问为啥收集,这可是你实战出的经验体现,武功秘籍。面试的时候基本会问,请问老弟你在项目中遇到叻哪些困难点呢?你说没有?那面试也基本就到这叻。你又要说有可能真的没有,有可能参加面试的人就是一个大手子。PI,就是真的大手子,公司招不起,并且公司大概率不敢去赌一个摸鱼大手子。日常多翻看翻看自己的武功秘籍。话说回来,现在的语雀很方便, Mac 与 Windows 双端都有客户端。你写的武功秘籍,还可以在地铁上用 WX小程序 里面的 语雀 观看。一日又一日的说自己没进步,那你 公司摸鱼 、 地铁中抖音与B站 、 到家P站 ,虽然也是学习,但我们学习不能过于碎片化。末尾相劝我们还年轻,是后浪,学习是一点一点堆起来的。不是今天说去学习设计模式,就要一下子把二十多个设计模式全学完,或者一下子学完。循序渐进,你就花五天,就在公司,就只学一种设计模式,学完就用上。周末抽出来半小时复习看一下就好,你说周末没时间,打游戏呢。那你在周一去公司通勤的路上,用手机看一下你的武功秘籍复习一下可好。
1.自我介绍 简历开篇就是介绍自己,忌长篇大论,那么点时间,相信你自己作为面试官,也不想去看阅读理解 忌讳 不要写杂事 获得了什么证书之类,像计算机几级或者mysql认证证书什么的 4、6级英语及个别例如acm奖项等,可写 之前见有人写去养老院献爱心之类的,确实,人不错,但是和你的工作联系不是那么大 建议 条理清晰,最好有序号 学历高要写前边,例如硕士,低于本科的话就先不要写,放在整体简历的最后 示例 简要,该体现的基本全都体现了,也可以放上去自己的籍贯,碰上老乡也是一种幸运 2. 技能描述 为何把这一项放在第二处? 因为你的技能,是占比最高的,例如公司想招一个会Spring Cloud和 Docker的,你全都没写,或者写在了后面,这就是重心没放在重点上,因为你做的项目很大概率和新公司的并不相同,甚至跨行业,技术才是通用的,所以建议放在第二处。 建议 做过多久的后端或者前端开发,或是全栈,这个也是面试官最想了解你的 技术栈的先后顺序,例如你想做后端,那就要先写后端的技术,前端的技术你会,那就是锦上添花 不要写精通,除非你自己觉得真的精通了,山外有山 像一些技术,例如Vue、React你只是自己去看博客或者视频,写了一个demo,不要去写熟练,写了解,不然碰到个练家子,基本就会问你生命周期与组件等 写一个框架或者一门技术时,一定一定去看看面试题,不然问到一些稍微深入的地方,你会懵逼 想到哪里先写到哪里,后面的想起来再去写一篇补充 一份Java技术栈示例 上面的技术没什么新鲜的,但是一条一条罗列出来,貌似比写证书什么的有用,当然,如果你熟悉热门的技术,例如 Python 爬虫、K8S、Spark、Oauth等等都是可以自行组装的,再次强调,不要写精通,技术面试官程序猿,或多或少都是心里有点犟的,你写个精通,今天不给你上一课,就不爽 3.工作经历 这个如果有大厂经历,可以替换掉第二处,优先级更上一层 其实也是写写时间点,公司职位,尽量用数字或者比率来写工作内容 反例 上海xxxxx有限公司 送外卖的 20xx.10-20xx.10 送送外卖,一天能送多少单,好评率达99% 上海xxxxx有限公司 Java研发 20xx.3-20xx.3 整理需求文档,设计数据库,搭框架学习,独立负责后端模块 前一个是不要写与你要找工作无关的工作经历,后者是太平常了,大家都这么写了 正常 北京****公司 JAVA 开发工程师 20xx.09-20xx.03 通过自学设计模式与数据库调优参与公司旧项目重构,完成后项目整体性能提升20% 在职期间,新项目的单元测试覆盖率达到80%,文档需求整理明确。 北京****公司 前端开发工程师 20xx.09-20xx.03 通过使用新技术Vue对公司旧项目重构,完成后项目页面流畅度提升20% 在职期间,对新项目的组件模块化,划分明确,文档需求整理明确。 这里你所写的,就要落实了,一般就会问你用到了哪些设计模式,还有数据库调优的解决方案,细化到如设计模式解决了哪些问题,数据库索引等 前端就会涉及到模块的封装与重用,前端页面的缓存与热重载问题 4.项目经历 列出两个常见的且容易直接pass的项目 4.1 商城 不要写仿京东商城、易买网、 xx商城、信用贷,关于商城的还是不要写了,为什么? 除非你有真实的去亲身做过,比如举例一个购物车 写了购物车的后果就是..... 还写么,没做过的就要考虑考虑了,是非能够自圆其说,所以还是自己做过什么就去写什么,真想包装,也请拿一些简单的业务去写,当然了,自己也要去做,亲身体验一个业务,不是看几个文章就能理解的 4.2 进销存 不是说不能写,一定要真的做过才要写,这个业务更为复杂,然后你就在项目模块三言两语给带过了看到这里,估计会有人讲了,这也不让写那也不让写,到底写什么,其实,这个不是我说了算,是你说了算的,总之,如果就是你自己单独下班手撸一个项目,只要你对业务熟悉的很,一样,能做到这个程度的人,一样不一般 简历模板推荐 其实这个写的可以参考格式,格式清晰的话面试官才有看下去的欲望,相信谁也不想去看一个花里胡哨的简历,排版要简洁,简洁而不失优雅 tips:最会问的就是你在项目遇到了什么难点,怎么解决的 ? 可笑的是很多都答不上,或者说没遇到过难点,这就有点厉害了,要么是真的厉害,要么就是没工作经验或者整天用最笨的代码加最狠的班 举个栗子: 项目中使用了try catch未捕捉到操作出错,导致数据库未回滚,从而关联到后续的操作数据不对,后面就直接抛出错误,异常使用全局AOP进行处理,这样还可以统一管理日志,以及监控方法的执行时间 自我评价 不要上来就写活泼开朗,自信阳光,这是个找技术的工作,不是让你去当公关月入三万 ,就平常写,爱好钻研技术不挺好的吗 有的写了爱好看技术书籍,问了也不知道看的哪一本,名字叫什么 Github上最好有点提交记录什么的,或者有点东西在往简历写吧 喜欢锻炼健身,挺好的,不过与工作无关的还是不推荐写 抗压能力强,和同事相处好,接受加班服从上级指挥,你不喜欢加班就不要写了,就写和同事相处好,服从上级指挥就行了 教育经历 没什么好写的,一些技术方面的证书也不要往上面写,目前行业除了事业单位基本不认同这个,技术+业务能力才是重要的 总结:全文字也比较多一点,如果你真的想写好简历,花一点耐心去看,希望能给你提供一点帮助,没有帮助,也不要喷我,我不想和你对线,对以上内容有不同想法的关注公众号《Java Pro》回复找弟弟即可与我对线 模板获取:关注公众号《Java Pro》回复简历模板即可获取上面图示中的模板
最常用的 Java 8 中的 Lambda 函数(项目中实用笔记) 简介 Java 8 中的新特性,虽然现在都出到了Java14版本,不过在日常的开发过程中,8的版本是足够使用了,再说现在的8以上的版本也都面向商业收费了,很多新手,我所接触到的,像我那时候一样,追求船新版本,一上来就去学java14的东西,当成一个爱好还行,重心还是要放在实用上 过滤 需求:我需要过滤高考分数大于500的人 首先,新建一个内部类 static class Student{ private String name; private Integer score; public String getName() { return name; } public void setName(String name) { this.name = name!=null ? name.trim() : null; } public Integer getScore() { return score; } public void setScore(Integer score) { this.score = score; } public Student(String name, Integer score) { this.name = name; this.score = score; } @Override public String toString() { return "Student{" + "name='" + name + '\'' + ", score=" + score + "}\n"; } } 使用IntStream遍历快速初始化一批值 public static void main(String[] args) { List<Student> studentList = IntStream.rangeClosed(0,20) .mapToObj(i -> new Student("Java Pro"+i,490+i)) .collect(Collectors.toList()); } 过滤出分数大于500的并输出 List<Student> studentGiao = studentList.stream() .filter(student -> student.score > 500) .collect(Collectors.toList()); System.out.println(studentGiao.toString()); 输出: [Student{name='Java Pro11', score=501} , Student{name='Java Pro12', score=502} , Student{name='Java Pro13', score=503} , Student{name='Java Pro14', score=504} , Student{name='Java Pro15', score=505} , Student{name='Java Pro16', score=506} , Student{name='Java Pro17', score=507} , Student{name='Java Pro18', score=508} , Student{name='Java Pro19', score=509} , Student{name='Java Pro20', score=510} ] 日常求和 需要考虑到为空和为0的情况 package com.github.gleans; import java.util.Arrays; import java.util.List; import java.util.Objects; public class LambdaLearning { public static void main(String[] args) { List<Double> nums = Arrays.asList(1.01, 2.11, 3.23, 4.222, null, 5.6); double resNum = nums.stream() .map(num -> Objects.isNull(num) ? 0 : num) .mapToDouble(num -> num) .sum(); System.out.println(resNum); } } map是重新指向一个对象,把->右侧的对象赋予,此处判断若num为null则赋予0值 注意,这里不可给null使用filter过滤掉,否则全为null的情况,会报空指针异常 扩展计算 public static void testTwo(){ List<Double> nums = Arrays.asList(1.01, 2.11, 3.23, 4.222, null, 5.6); DoubleSummaryStatistics number = nums.stream() .map(num -> Objects.isNull(num) ? 0 : num) .mapToDouble(num -> num) .summaryStatistics(); System.out.println("最大值:"+number.getMax()); System.out.println("最小值:"+number.getMin()); System.out.println("平均值:"+number.getAverage()); } 输出 最大值:5.6 最小值:0.0 平均值:2.6953333333333336 reduce简单使用 public static void main(String[] args) { testOne(); } public static void testOne(){ List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6); // 这里的 10 相当于初始值 int sum = numbers .stream() .reduce(10, Integer::sum); System.out.println(sum); } Collectors.groupingBy 根据年龄分组 package com.github.gleans; import java.math.BigDecimal; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.stream.Collectors; public class SumWage { public static void main(String[] args) { List<UserDemo> userDemoList = new ArrayList<UserDemo>() {{ add(new UserDemo(20, "jason", BigDecimal.valueOf(1000000))); add(new UserDemo(22, "yasuo", BigDecimal.valueOf(2000000))); add(new UserDemo(22, "ekko", BigDecimal.valueOf(100))); }}; Map<Integer, List<UserDemo>> UserDemoMapByAge = userDemoList.stream() .collect(Collectors.groupingBy(UserDemo::getAge)); System.out.println(UserDemoMapByAge.toString()); } static class UserDemo { private int age; private String username; private BigDecimal wage; public UserDemo(int age, String username, BigDecimal wage) { this.age = age; this.username = username; this.wage = wage; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public BigDecimal getWage() { return wage; } public void setWage(BigDecimal wage) { this.wage = wage; } @Override public String toString() { return "UserDemo{" + "age=" + age + ", username='" + username + '\'' + ", wage=" + wage + '}'; } } } 输出 {20=[UserDemo{age=20, username='jason', wage=1000000}], 22=[UserDemo{age=22, username='yasuo', wage=2000000}, UserDemo{age=22, username='ekko', wage=100}]} json化观看观看更为直观 { 20:[ { "age":20, "username":"jason", "wage":1000000 } ], 22:[ { "age":22, "username":"yasuo", "wage":2000000 }, { "age":22, "username":"ekko", "wage":100 } ] } 进阶计算 Collectors.summarizingDouble Map<Integer, DoubleSummaryStatistics> userAvgWageByAge = userDemoList.stream() .collect(Collectors.groupingBy(UserDemo::getAge, Collectors.summarizingDouble(s -> s.getWage().doubleValue()))); userAvgWageByAge.forEach((k, v) -> System.out.println(String.format("年龄:%d,平均工资:%f", k, v.getAverage()))); 数组快速转为List Stream.of(1, 2, 3, 4).collect(Collectors.toList()) 结论 后续会学习更多关于Lambda的操作,日积月累...一定会成为一个秃头的程序猿
都2020年了,你还不会写简洁的Java代码! 使用Google Guava依赖 <dependency> <groupId>com.google.guava</groupId> <artifactId>guava</artifactId> <version>29.0-jre</version> </dependency> 创建一个list集合并赋值 最原始的办法: List<String> stringList = new ArrayList<>(); stringList.add("jack"); stringList.add("pony"); stringList.add("ekko"); 简单改造: List<String> stringList2 = new ArrayList<String>(4) {{ add("jack"); add("pony"); add("ekko"); }}; 终极改造: List<String> stringList3 = ImmutableList.of("jack", "pony", "ekko"); 去除list中的空值 一般做法: List<String> nameList = new ArrayList<>(); List<String> noNullList = new ArrayList<>(); nameList.add("jack"); nameList.add("pony"); nameList.add("ekko"); nameList.add(null); for (String o : stringList) { if (o != null) { noNullList.add(o); } } 使用lamda简化后的写法: List<String> noNullListFun = nameList .stream() .filter(Objects::nonNull) .collect(Collectors.toList()); list中的值求和 原始做法: List<BigDecimal> numList = new ArrayList<BigDecimal>(10) {{ add(BigDecimal.valueOf(111L)); add(BigDecimal.valueOf(8888.22)); add(BigDecimal.valueOf(333.22)); add(BigDecimal.valueOf(857857.22)); add(BigDecimal.valueOf(5331.22)); }}; BigDecimal total = BigDecimal.ZERO; for (BigDecimal num : numList) { total = total.add(num); } System.out.println(total); 简化一下: List<BigDecimal> numListSimple = ImmutableList.of(BigDecimal.valueOf(111L) , BigDecimal.valueOf(8888.22), BigDecimal.valueOf(333.22) , BigDecimal.valueOf(857857.22), BigDecimal.valueOf(5331.22)); // 求和 BigDecimal totalNum = BigDecimal.valueOf(numListSimple.stream().mapToDouble(BigDecimal::doubleValue).sum()); 判断值是否为空 原始写法: public static String getUserName() { return null; } // 模拟调用服务 String username = getUserName(); String res; if (username != null) { res = username; } else { res = "游客"; } 简化: String userName = Optional.ofNullable(username).orElse("游客"); System.out.println(userName); 从多个集合中取匹配的值 原始方法: package com.github.springtools.test; import com.google.common.collect.ImmutableList; import java.util.HashSet; import java.util.List; import java.util.Set; public class TestFlatMap { public static void main(String[] args) { /* * 匹配到姓马的并去重然后放入一个新的集合中 */ List<String> nameListA = ImmutableList.of("素云云", "马云云", "腾花花", "阿娇娇", "马飞飞", "廖妹妹"); List<String> nameListB = ImmutableList.of("素云涛", "唐三三", "小五五", "马中山", "马僻静", "马肥羊"); List<String> nameListC = ImmutableList.of("张三", "李四", "王二", "麻子", "上官玩儿", "马可菠萝"); Set<String> nameSet = new HashSet<>(); nameListA.forEach(n -> { if (n.startsWith("马")) { nameSet.add(n); } }); nameListB.forEach(n -> { if (n.startsWith("马")) { nameSet.add(n); } }); nameListC.forEach(n -> { if (n.startsWith("马")) { nameSet.add(n); } }); System.out.println(nameSet.toString()); } } 改造后的代码: import com.google.common.collect.ImmutableList; import java.util.List; import java.util.Set; import java.util.stream.Collectors; import java.util.stream.Stream; public class TestFlatMap2 { public static void main(String[] args) { /* * 匹配到姓马的并去重然后放入一个新的集合中 */ List<String> nameListA = ImmutableList.of("素云云", "马云云", "腾花花", "阿娇娇", "马飞飞", "廖妹妹"); List<String> nameListB = ImmutableList.of("素云涛", "唐三三", "小五五", "马中山", "马僻静", "马肥羊"); List<String> nameListC = ImmutableList.of("张三", "李四", "王二", "麻子", "上官玩儿", "马可菠萝"); Set<String> nameSet = Stream.of(nameListA, nameListB, nameListC) .flatMap(list -> list.stream().filter(name -> name.startsWith("马"))) .collect(Collectors.toSet()); System.out.println(nameSet.toString()); } } 总结 Java之道,写的代码第一点是要别人能看的明白,不然你离职了留下一堆像shi一样的代码,接盘的人一定会问候你的亲人,何必呢二是要利用一切可以利用的工具类,不过用的时候看看别人的源码,写的时候考虑的周全与否,这样基本你的代码显性来看会少很多总之,功能一点点实现,大道至简,用最简单的代码实现最强的逻辑~
一文整懂 Java 中静态代码块 / 初始块 / 构造方法的执行顺序 “ 相信,刷过面试题应该都碰到过很多题,关于类继承后 Java 中静态代码块 / 初始块 / 构造方法的执行顺序问题,每每记一下又忘了,那么,今天来用不多的时间复习一下” 01.静态代码块 / 初始块 / 构造方法的执行顺序 package erdan.demo; public class Demo { static { System.out.println("静态代码块 111"); } static { System.out.println("静态代码块 222"); } { System.out.println("初始块 111"); } { System.out.println("初始块 222"); } public Demo() { System.out.println("无参构造方法"); } public Demo(int i) { System.out.println("有参构造方法" + i); } public static void main(String[] args) { new Demo(); } } 输出: 静态代码块 111 静态代码块 222 初始块 111 初始块 222 无参构造方法 上图: 笔记:初始块(非静态代码块)总是和构造方法是一家子,会一块出现 02.测试继承之后的执行顺序 package erdan.demo; public class ChildrenDemo extends Demo{ static { System.out.println("ChildrenDemo:静态代码块 111"); } static { System.out.println("ChildrenDemo:静态代码块 222"); } { System.out.println("ChildrenDemo 初始块 111"); } { System.out.println("ChildrenDemo 初始块 222"); } public ChildrenDemo() { System.out.println("无参构造方法 ChildrenDemo"); } public ChildrenDemo(int i) { System.out.println("有参构造方法 ChildrenDemo:" + i); } public static void main(String[] args) { new ChildrenDemo(); System.out.println("---------------------------------"); new ChildrenDemo(1); } } 输出: 静态代码块 111 静态代码块 222 ChildrenDemo:静态代码块 111 ChildrenDemo:静态代码块 222 初始块 111 初始块 222 无参构造方法 ChildrenDemo 初始块 111 ChildrenDemo 初始块 222 无参构造方法 ChildrenDemo --------------------------------- 初始块 111 初始块 222 无参构造方法 ChildrenDemo 初始块 111 ChildrenDemo 初始块 222 有参构造方法 ChildrenDemo:1 可以看到下面除了调用有参无参的差别外,虚线下比上面少了静态代码块 ps: 类比子类先执行 态代码块只会输出一次,初始块与构造方法是一家子,输出在一块,初始块会在构造前初始化 03.总结 父类比子类先行执行 静态代码块,在类第一次加载的时候,会初始化一次,适合项目中初始化全局参数,常量等 初始块与构造方法是一家子,但是初始块会在构造函数前执行,初始块适合重载构造函数存在相同代码,可以抽出来使用
IDEA提高开发效率的7个插件 1. 多行编辑 先来体验一下从xml文件拷贝字段新建实体对象一般我们为了新建多表连接后映射的 ResultMap ,耗费不少时间,那么我们就来试一试这个多行编辑 表字段存在下划线,而实体中不允许,更是讨厌 ,等着一招教你解决 前提条件,安装一个idea的插件,用来驼峰与下划线互转的:CamelCase步骤:① 多行选择,按住ALT(windows)/option(Mac) ,拉动鼠标就可② 选中字段对象 Win Ctrl+shift+左箭头 Mac option+shift+左箭头 ③ 复制,然后新建实体对象,右键选择 Paste without Formatting,也就是无格式粘贴④ 然后下划线转驼峰对象,插件有快捷键 Win Shift + Alt + U Mac ⇧(shift) + ⌥(option) + U ⑤ 选中多行,直接输入即是多行编辑,编辑完成后使用代码格式化即可 2. Grep Console 功效 自定义idea日志打印颜色等功效 解决的问题 历史的老项目打印的日志无颜色,未配置颜色 多项目配置的彩色日志也不同 其他地方日志粘贴过来,也可以上色 直接去idea设置中 Plugins 下载即可,下面设置日志颜色效果图如下 3. JRebel热部署 解决了什么问题 项目启动时间长,修改一个字段要重启一下,实属弟弟 boot自带的devtools,需要每个项目都来那么一下 修改xml中SQL,改一下,也需要重启 作用与说明 修改除了配置以外的类与xml或jsp等,无需重启 如果使用了Mybatis Plus,还请安装上图第二个插件 使用配置 idea菜单Help -> JRebel -> Activation 有钱的请支持正版,冇钱的Google下JRebel激活教程也能找到,各凭本事,在此就不多讲了,开始讲使用 1.开启自动编译 Win Ctrl+shift+alt+/ Mac ctrl+shift+option+/ 然后点击第一个 1.Registry...,勾选 complier.automake....2.找到 IDE 侧边栏的JRebel -> Panel,选中3.编辑启动配置文件勾选如下4.点击以 JRebel 启动 左边的是直接运行 右边的是以 DeBUG启动,热部署在DeBUG模式启动才有效果 4. Translation插件 解决了什么问题 文字翻译,例如 Java 中 api 的注释你看不懂,选中就可翻译对象命名翻译替换工具,再也不为对象命名发愁 有些人编码时命名变量,命名方法,千奇百怪,可以说是前无古人,后无来者。——周树人 说明 多个翻译引擎 谷歌翻译 有道翻译 百度翻译 多种语言的互译 文字转语音 自动选择字 自动分词 使用 选中需要翻译或者替换命名的文字 示例 翻译外语 2.替换命名 5. RestfulToolkit 功效 根据接口搜索 提供接口可以测试 实测 根据接口进行搜索 Win Ctrl + / Mac command + / 侧边栏找到 RestServices 提供了所有 Controller 里的接口,还有默认的测试数据 6. Lombok 解决的问题 萌新:小哥,我在实体类写了那么多get/set方法,看着很迷茫小哥:那不是可以自动生成吗?萌新:虽然可以自动生成,但是如果我要修改某个变量的数据类型,我岂不是还要去修改get/set方法?小哥:哈哈,那我今天给你说一个插件,lombok可以解决你的问题 @Data 可以代替getter/setter方法 @NoArgsConstructor 生成无参构造 @AllArgsConstructor 生成全参构造 安装Lombok 1.在Intellij IDEA中安装lombok插件,File ->Setting -> Plugins 项目开启注解 7. IDEA 官方汉化插件(仅适用2020之后的版本)
SpringBoot2.x与MongoDB集成(十) 准备工作 介绍MongoDB与Spring Boo集成,并通过MongoRepository以及MongoTemplate来执行CRUD操作。 Spring Boot:2.3.0.RELEASESpring Boot Data MongoDB:2.3.0.RELEASEMongoDB:4.2.6MongoDB Driver:4.0.3 要操作的集合示例数据结构(即model) Department:String idString nameString descriptionArray employees Employee:String empIdString nameint agedouble salary one to many relationships 安装MongoDB MongoDB快速入门指南与docker-compose快体验 https://www.yuque.com/ekko/database/dkluyg 创建Spring Boot项目 SpringBoot2.x快速入门指南(一) https://www.yuque.com/ekko/spring/qqt7xd 添加Mongo依赖 <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-mongodb</artifactId> </dependency> Spring application配置 application.properties spring.data.mongodb.username=root spring.data.mongodb.password=example spring.data.mongodb.database=admin 测试能否正常启动 host:localhostport:27017database:adminuser:rootpassword:example 项目结构 创建Domain Object package com.example.mongodb.model; import org.springframework.data.annotation.Id; import org.springframework.data.mongodb.core.index.Indexed; import org.springframework.data.mongodb.core.mapping.Document; import java.util.List; @Document("Department") public class Department { @Id private String id; @Indexed(name = "deptName") private String name; private String description; @DBRef private List<Employee> employees; //...getter setter } package com.example.mongodb.model; import org.springframework.data.annotation.Id; import org.springframework.data.mongodb.core.mapping.Document; @Document("Employee") public class Employee { @Id private String empId; private String name; private int age; private double salary; //...getter setter } @Document 标识了要持久化到mongodb的DO。@Id 文档的唯一标识,在mongodb中是objectId。@DbRef 关联另一个Document对象,存入的是文档的引用,如果不使用这个注解,存入的是内容。不过即使使用@DbRef,mongodb本身并不维护关联数据,也就是说需要手动将数据插入到被关联文档。 两种操作数据的方式 MongoRepository package com.example.mongodb.repository; import com.example.mongodb.model.Department; import org.springframework.data.mongodb.repository.MongoRepository; import org.springframework.data.mongodb.repository.Query; import org.springframework.stereotype.Repository; import java.util.List; @Repository public interface DepartmentRepository extends MongoRepository<Department,String> { @Query(value = "{'Employee.name': ?0}", fields = "{'employees' : 0}") Department findDepartmentByEmployeeName(String empName); List<Department> findDepartmentByName(String name); } MongoTemplate package com.example.mongodb.repository; import com.example.mongodb.model.Department; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.mongodb.core.MongoTemplate; import org.springframework.data.mongodb.core.query.Criteria; import org.springframework.data.mongodb.core.query.Query; import org.springframework.data.mongodb.core.query.Update; import org.springframework.stereotype.Repository; import java.util.List; @Repository public class DeptRepository { @Autowired private MongoTemplate mongoTemplate; public List<Department> findAll() { return mongoTemplate.findAll(Department.class); } public List<Department> findDepartmentByName(String deptName){ Query query = new Query(); query.addCriteria(Criteria.where("name").is(deptName)); return mongoTemplate.find(query, Department.class); } public Department save(Department department) { mongoTemplate.save(department); return department; } public Department update(Department department){ Query query = new Query(); query.addCriteria(Criteria.where("id").is(department.getId())); Update update = new Update(); update.set("name", department.getName()); update.set("description", department.getDescription()); return mongoTemplate.findAndModify(query, update, Department.class); } public void deleteById(String deptId) { Query query = new Query(); query.addCriteria(Criteria.where("id").is(deptId)); mongoTemplate.remove(query, Department.class); } } 创建Controller package com.example.mongodb.controller; import com.example.mongodb.model.Department; import com.example.mongodb.model.Employee; import com.example.mongodb.repository.DepartmentRepository; import com.example.mongodb.repository.DeptRepository; import com.example.mongodb.repository.EmpRepository; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.*; import java.util.Collections; import java.util.List; import java.util.Optional; @RestController public class DepartmentController { @Autowired DepartmentRepository departmentRepository; @Autowired DeptRepository deptRepository; @Autowired EmpRepository empRepository; @PostMapping("/v1/dept/s") public Department v1save(@RequestBody Department department) { List<Employee> employees = Optional.ofNullable(department.getEmployees()).orElse(Collections.emptyList()); employees.forEach(employee -> empRepository.save(employee)); return departmentRepository.save(department); } @GetMapping("/v1/dept/l") public List<Department> v1list(){ return departmentRepository.findAll(); } @PutMapping("/v1/dept/u/{deptId}") public Department v1update(@RequestBody Department department, @PathVariable String deptId) { department.setId(deptId); List<Employee> employees = Optional.ofNullable(department.getEmployees()).orElse(Collections.emptyList()); employees.forEach(employee -> empRepository.save(employee)); return departmentRepository.save(department); } @DeleteMapping("/v1/dept/d/{deptId}") public String v1delete(@PathVariable String deptId) { departmentRepository.deleteById(deptId); return deptId; } @GetMapping("/v1/dept/get/{deptName}") public List<Department> v1getByName(@PathVariable String deptName) { return departmentRepository.findDepartmentByName(deptName); } @GetMapping("/v1/dept/get/emp/{empName}") public Department v1getByEmpName(@PathVariable String empName) { return departmentRepository.findDepartmentByEmployeeName(empName); } @PostMapping("/v2/dept/s") public Department v2save(Department department) { List<Employee> employees = Optional.ofNullable(department.getEmployees()).orElse(Collections.emptyList()); employees.forEach(employee -> empRepository.save(employee)); return deptRepository.save(department); } @GetMapping("/v2/dept/l") public List<Department> v2list() { return deptRepository.findAll(); } @PutMapping("/v2/dept/u") public Department v2update(Department department){ List<Employee> employees = Optional.ofNullable(department.getEmployees()).orElse(Collections.emptyList()); employees.forEach(employee -> empRepository.save(employee)); return deptRepository.update(department); } @DeleteMapping("/v2/dept/d/{deptId}") public void v2delete(@PathVariable String deptId) { deptRepository.deleteById(deptId); } @GetMapping("/v2/dept/get/{deptName}") public List<Department> v2getByName(@PathVariable String deptName){ return deptRepository.findDepartmentByName(deptName); } } v1的接口使用MongoRepository方式操作数据,v2的接口使用MonoTemplate方式操作数据。 测试 ###新增 POST http://localhost:8080/v1/dept/s Content-Type: application/json { "id": "10010", "name": "中国联通", "description": "中国联通", "employees": [ { "empId": "1", "name": "jack ma", "age": 78, "salary": 900000.0 } ] } ###更新 PUT http://localhost:8080/v1/dept/u/10010 Content-Type: application/json { "id": "10010", "name": "中国联通", "description": "中国联通", "employees": [ { "empId": "1", "name": "jack ma", "age": 78, "salary": 900000.0 }, { "empId": "2", "name": "pony ma", "age": 78, "salary": 900000.0 } ] } ###删除 DELETE http://localhost:8080/v1/dept/d/10010 ###查询 GET http://localhost:8080/v1/dept/l 项目地址 持续更新地址 作者: rainbow unicorn
基于postman测试接口(整套接口测试) 可以解决的问题 几百个接口人工测试接口过于繁杂 大多测试无法使用请求结果当参数 可以使用随机参数 支持swagger信息导入 随账号持久化保存数据 对集合一键测试 自定义可视化结果 开启控制台 单个测试尝试 使用请求结果当参数 pm.test("存全局参数", function () { // 获取json var jsonData = pm.response.json(); pm.globals.set("param_name",jsonData.data.name); }); 在下一个接口使用 param_name 可以看到name被替换掉了 打印结果(JSON) 自定义可视化结果 在 Tests 里编写 var template = ` <table bgcolor="#FFFFFF"> <tr> <th>Name</th> <th>Email</th> </tr> {{#each response}} <tr> <td>{{name}}</td> <td>{{email}}</td> </tr> {{/each}} </table> `; // 设置 可视化器 pm.visualizer.set(template, { // 这里拿的返回结果里的参数 response: pm.response.json().data.array }); 随机参数 {{$guid}}:添加一个V4风格GUID(如: aa002-44ac-45ca-aae3-52bf19650e2d) {{$timestamp}}:将当前的时间戳,精确到秒 {{$randomInt}}:添加0和1000之间的随机整数 当前时间戳13位 postman.setGlobalVariable("time13", new Date().getTime()); // 或者 pm.globals.set("time13", new Date().getTime()); js获取常用时间链接:https://www.yuque.com/ekko/front-dev/bpvn6x 测试用例连接 我的测试链接实例: https://www.getpostman.com/collections/94a58d113d80dd14b63d 一键测试接口集合 测试结果,这样的话就完成了一个完完整整的接口测试 从swagger导入接口 测试swagger地址,这里版本2.0,没测试3.0http://petstore.swagger.io/v2/swagger.json点击 continue 继续只选择 Collection 然后点击 Import 这里可以看到导入的测试集合 自定义全局变量 用来存储api地址,以及常用的参数 持续更新链接
MongoDB快速入门指南与docker-compose快体验 MongoDB相对于RDBMS的优势 模式少 -MongoDB是一个文档数据库,其中一个集合包含不同的文档。一个文档之间的字段数,内容和文档大小可能会有所不同。 单个对象的结构清晰。 没有复杂的联接。 深入的查询能力。MongoDB支持使用与SQL几乎一样强大的基于文档的查询语言对文档进行动态查询。 sql Tuning(优化) 易于扩展 不需要将应用程序对象转换/映射到数据库对象。 使用内部存储器存储(窗口式)工作集,从而可以更快地访问数据 RDBMS:关系数据库管理系统 为什么要使用MongoDB 面向文档的存储,BSON格式存储,即Binary JSON 单键索引、复合索引、多键索引、地理空间索引、全文本索引和哈希索引 MongoDB实现高可用 主从复制 自动分片 auto sharding 丰富的查询 快速原地更新(fast in-place updates):大部分更新操作无需申请新空间 MongoDB的专业支持map/reduce支持 Gridfs:各种size大小的集群文件支持 在哪里使用MongoDB 大数据 内容管理和交付 移动和社交基础设施 用户数据管理 数据中心 docker-compose 快速启动 MongoDB docker-compose.yml version: '3' services: mongodb: image: mongo:4.2.6 # 镜像:版本 container_name: mongo_db environment: - MONGO_INITDB_DATABASE=默认的数据库 - MONGO_INITDB_ROOT_USERNAME=你的root管理员名称 - MONGO_INITDB_ROOT_PASSWORD=你的root管理员名称密码 volumes: - ./mongo/init-mongo.js:/docker-entrypoint-initdb.d/init-mongo.js:ro - ./mongo/mongo-volume:/data/db ports: - "27017-27019:27017-27019" restart: always init-mongo.js // db.getSiblingDB() 相当于 use admin; db.getSiblingDB('admin') .createUser({ user: 'user', pwd: 'user', roles: ['readWrite'] }); 然后执行命令 docker-compose up -d Navicat Premium连接 填入上述 docker-compose.yml 对应的参数测试连接,测试成功如果你的不显示,去 navicat ==》 菜单 ==》 显示 ==》钩上显示隐藏的项目 重启navicat就可以了 打开一个集合右下角可以查看 网格视图 树视图 JSON视图 增删改查 1.创建操作 db.collection.insertOne() db.collection.insertMany() // 插入集合名称为products的集合并插入数据 (不存在集合会新建) db.products.insertOne({ item: "card", qty: 15 }); // 批量插入数据 db.products.insertMany([{ item: "card1", qty: 16 }, { item: "card2", qty: 17 }, { item: "envelope", qty: 20 }, { item: "stamps", qty: 30 }]); 2.查询操作 db.collection.find() db.products.find({ qty: 15 }).limit(1) 3.更新操作 db.collection.updateOne() db.collection.updateOne(filter,update,options) 查找与过滤器匹配的第一个文档,并应用指定的更新修改。 db.products.find({ item: "stamps" }).limit(1); // 找到item为stamps的并修改其qty为60 db.products.updateOne( { "item": "stamps" }, { $set: { "qty": 60 } } ); db.products.find({ item: "stamps" }).limit(1); db.collection.updateMany() // 把qty大于15的统统修改为15 db.products.updateMany( { qty: { $gt: 15 } }, { $set: { "qty": 15 } } ); db.collection.replaceOne() 使用替换文档替换集合中与过滤器匹配的第一个匹配文档。 // 把集合products中item为stamps 整个替换掉 db.products.replaceOne( { "item" : "stamps" }, { "item" : "stampss", "Borough" : "Manhattan" } ); 4.删除操作 db.collection.deleteOne() db.collection.deleteMany() // 删除item为stampss的 db.products.deleteOne( { "item" : "stampss" } ); // 批量删除qty小于15的 db.products.deleteMany( { "qty" : {$lt: 15} } ); 持续更新连接
SpringBoot2.x快速入门指南(一) 准备工作 IDE: IntelliJ IDEA 2020.3 Java环境 jdk1.8 在官网快速创建SpringBoot项目 下面开始进入正题:进入 https://start.spring.io/ 生成一个初始项目这里会下载一个zip的项目压缩包 使用 Maven 导入 Spring Boot 项目 demo.zip解压之后记得复制下demo文件夹放的路径在此用的开发工具是IntelliJ IDEA下面是导入流程:IDEA里点击File -> Open -> 粘贴刚刚的demo文件夹路径 -> 找到 pom.xml 双击-> Open as Peoject -> 等待 Maven 加载完就好,看不明白看下图出现下面这个直接 fix 掉,未出现不用理睬 Maven切换国内源 你会发现他那个依赖下载的特别慢要么你科学上网下一步,关掉它然后重新打开在 pom.xml 文件底部增加 <repositories> <!--阿里云主仓库,代理了maven central和jcenter仓库--> <repository> <id>aliyun</id> <name>aliyun</name> <url>https://maven.aliyun.com/repository/public</url> <releases> <enabled>true</enabled> </releases> <snapshots> <enabled>false</enabled> </snapshots> </repository> <!--阿里云代理Spring 官方仓库--> <repository> <id>spring-milestones</id> <name>Spring Milestones</name> <url>https://maven.aliyun.com/repository/spring</url> <releases> <enabled>true</enabled> </releases> <snapshots> <enabled>false</enabled> </snapshots> </repository> </repositories> <pluginRepositories> <!--阿里云代理Spring 插件仓库--> <pluginRepository> <id>spring-plugin</id> <name>spring-plugin</name> <url>https://maven.aliyun.com/repository/spring-plugin</url> <releases> <enabled>true</enabled> </releases> <snapshots> <enabled>false</enabled> </snapshots> </pluginRepository> </pluginRepositories> 起飞,等待下载完成即可 SpringBoot项目启动 启动前准备依据下图把 DemoApplication 启动类 移到包最外层启动类相当于管理项目的负责人,你把他扔到与控制层同级肯定出错不是目的是打开一个web应用,在 pom.xml 中 <dependencies> 下增加 <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> 修改 BootDemoApplication.java package com.example.demo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; /* * * description : 这里的@RestController相当于 @ResponseBody+@Controller * 使用@RestController 相当于使每个方法都加上了 @ResponseBody 注解 * @author: Robot * @date 0:03 2020/5/8 **/ @RestController @SpringBootApplication public class BootDemoApplication { public static void main(String[] args) { SpringApplication.run(BootDemoApplication.class, args); } /** * 这里的@GetMapping相当于@RequestMapping(value = "/hello", method = RequestMethod.GET) **/ @GetMapping("hello") public String test(){ return "i love java"; } } 项目结构如下 推荐调试模式启动,往后我们做项目也是 启动成功之后访问 http://localhost:8080/hello上图成功代表项目可以访问了,你成功的迈向 Spring 第一步 配置application.yml 什么是yml?YML文件格式是YAML (YAML Aint Markup Language)编写的文件格式,YAML是一种直观的能够被电脑识别的的数据数据序列化格式,并且容易被人类阅读,容易和脚本语言交互的,可以被支持YAML库的不同的编程语言程序导入,比如: C/C++, Ruby, Python, Java, Perl, C#, PHP等。听不懂吧,其实我也看不明白就是相当于xml,properties的配置文件,看的更直观,上代码吧还是 下述properties spring.resources.locations= classpath:/templates 改为yml格式之后 spring: resources: static-locations: classpath:/templates yml需要注意,冒号(:)后面要跟空格,第二级和第一级要在上下行用一个Tab的距离 application.yml server: port: 8080 # 连接数据库的,需要存在可以使用的数据库,不然报错 spring: datasource: type: com.alibaba.druid.pool.DruidDataSource driver-class-name: com.mysql.jdbc.Driver url: jdbc:mysql://127.0.0.1:3306/dovis?characterEncoding=utf-8 username: root password: root 永久更新地址 https://www.yuque.com/ekko/spring/qqt7xd
2021年08月
2021年07月
2020年07月
2020年06月