写点什么

java 部分基础总结

作者:Studying_swz
  • 2022-10-18
    天津
  • 本文字数:5648 字

    阅读完需:约 1 分钟

java部分基础总结

----------------------java 部分难点------------------------

1 .抽象类和接口

抽象类:体现的是一种模板的思想接口:体现的是一种规范的思想


相同点:


  • 1:两者都不能实例化

  • 2:一个类实现了某个接口或者继承了某个抽象类,必须对其中所有的抽象方法全部实现,否则仍然需要声明为抽象类。

  • 3:都可以作为引用,多态的思想。


不同点:


  • 1.方法:抽象类:可以有构造函数、普通方法、静态方法;包含抽象方法的一定是抽象类,但是抽象类不一定含有抽象方法;接口:只能有 public abstart 方法,不能含有普通方法,1.8 之后可以有 default 方法并且可以有静态方法。


public interface Person{  public static final int a=10;  //JDK1.8    default void sayHello(){        System.out.println("Hello World");    }    public void say();}
复制代码


  • 2.访问权限抽象类可以是 public、protected、default 等权限,而接口只能是 public static final?为什么?public:接口可以被不同包的不同类实现,是公有的,static:一个类继承多个接口时,即使存在同名变量也不会混淆。如果每个接口都含有一个名为 a 的成员变量,那么在该类中可以通过 接口 1.a, 接口 2.a, 接口 3.a 来调用 a,final:该变量不可被修改。如果一个接口被多个类实现时,在类 1 中修改了该变量的值,那么其他类中该变量的值也会变化,要防止该情况的发生。

  • 3.继承/实现抽象类单继承、接口可以被多实现。


<hr style=" border:solid; width:100px; height:1px;" color=#000000 size=1">

2 .内部类、静态内部类、局部内部类、匿名内部类

  • 内部类:对象的创建需要依赖父对象才能创建;可以访问类的所有成员;不能有静态方法,因为其是懒加载的,所以如果直接用类名访问,会报错。


  class Circle {      private double radius = 0;      public static int count =1;      public Circle(double radius) {          this.radius = radius;      }             class Draw {     //内部类          public void drawSahpe() {              System.out.println(radius);  //外部类的private成员              System.out.println(count);   //外部类的静态成员          }      }      public static void main(String[] args)  {          Circle outter = new Circle();          Circle.Draw inner = outter.new Draw();  //必须通过r对象来创建      }   }
复制代码


  • 静态内部类:对象的创建不需要有外部类的对象;只能访问父类的静态成员;内部可以有静态、非静态方法。


  class Circle {      private double radius = 0;      public static int count =1;      public Circle(double radius) {          this.radius = radius;      }             static class Draw {     //内部类          public void drawSahpe() {             /// System.out.println(radius);  //外部类的private成员              System.out.println(count);   //外部类的静态成员          }      }      public static void main(String[] args)  {                     Circle.Draw inner = new Circle.Draw();      }   }
复制代码


  • 局部内部类:方法中声明一个类;直接可以访问外部类的所有成员,同非静态内部类;方法中的变量必须是 final 的,因为如果局部内部类对象引用了该变量,而该方法在虚拟机栈弹出(一个栈帧),所以会清除,而常量会保持在堆中(常量池),不会随着栈的方法的弹出而销毁。


  class People{      public People() {                 }  }     class Man{      public Man(){                 }             public People getWoman(){        final int x = 5;          class Woman extends People{   //局部内部类              int age =x;          }          return new Woman();      }  }
复制代码


  • 匿名内部类 匿名内部类是唯一一种没有构造器的类。正因为其没有构造器,所以匿名内部类的使用范围非常有限,大部分匿名内部类用于接口回调。


  interface Person(){    //可以有多个方法    void play();  }    class Test(){    public static void main(){      //匿名内部类,创建匿名对象      Person p = new Person(){        @override        public void play(){           System.out.println("123");      }      //函数式接口      Person p = ()-> System.out.println("123");    }  }
复制代码


<hr style=" border:solid; width:100px; height:1px;" color=#000000 size=1">

3 .AtomicStampedReference 类

  • 用于解决 ABA 问题

  • 代码结构:


  //构造方法, 传入引用和戳  public AtomicStampedReference(V initialRef, int initialStamp)  //返回引用  public V getReference()  //返回版本戳  public int getStamp()  //如果当前引用 等于 预期值并且 当前版本戳等于预期版本戳, 将更新新的引用和新的版本戳到内存  public boolean compareAndSet(V   expectedReference,                                   V   newReference,                                   int expectedStamp,                                   int newStamp)  //如果当前引用 等于 预期引用, 将更新新的版本戳到内存  public boolean attemptStamp(V expectedReference, int newStamp)  //设置当前引用的新引用和版本戳  public void set(V newReference, int newStamp) 
复制代码


  • 原理:在 AtomicInteger 的基础上增加了一个版本号,用来进行进一步的标识,当然整体是由乐观锁操作 CAS 实现的,然后又加了一个版本号的判断。


<hr style=" border:solid; width:100px; height:1px;" color=#000000 size=1">

4.复制算法的问题?

  • 仅针对新生代效果好,浪费内存(只能用一半);

  • 转移存活对象时会导致用户线程无法定位引用对象。(这点之前没有想到)


<hr style=" border:solid; width:100px; height:1px;" color=#000000 size=1">

5.String 的“+”符号的理解

  • 源码


  String s1 = "ouyangjun";  String s2 = "p812438109";  System.out.println(s1 + s2); // ouyangjunp812438109
复制代码


  • 反编译


  System.out.println(new StringBuilder(String.valueOf(s1)).append(s2).toString());
复制代码


  • 结论:编译时,先 new 一个 StringBuilder 对象,再把参数 append 到对象中,最后再 toString。在用"+"号拼接时,不管是什么方式拼接,都是会不断 new StringBuilder 对象。如循环次数越多,该拼接方式性能会越低,最终可能导致内存溢出等问题。现在程序中都不建议用该方式拼接字符串了、

  • “+”原本是运算符,为什么可以用来拼接字符串?运算符重载。(这里是有争议的,Java 本身是不支持运算符重载的,String 的+操作实际上 String 类作者设计的语法糖。还可以追问为啥 Java 不给支持运算符重载?我猜是性能问题)参考链接:https://blog.csdn.net/p812438109/article/details/103107624

  • 语法糖语法糖(Syntactic sugar),也译为糖衣语法,是由英国计算机科学家彼得·兰丁发明的一个术语,指计算机语言中添加的某种语法,这种语法对语言的功能没有影响,但是更方便程序员使用。语法糖让程序更加简洁,有更高的可读性。参考链接:https://www.cnblogs.com/qingshanli/p/9375040.html


<hr style=" border:solid; width:100px; height:1px;" color=#000000 size=1">

6.JIT 简单理解

即时编译器:对象不一定会分配至堆上


  • 逃逸分析:当一个对象在方法中被定义后,它可能被外部方法所引用,例如作为调用参数传递到其他地方中,称为方法逃逸。


public static StringBuffer craeteStringBuffer(String s1, String s2) {   StringBuffer sb = new StringBuffer();   sb.append(s1);   sb.append(s2);   return sb;}
复制代码


但是如果这样的话,就不会逃逸出去


public static String createStringBuffer(String s1, String s2) {   StringBuffer sb = new StringBuffer();   sb.append(s1);   sb.append(s2);   return sb.toString();}
复制代码


所以此时编译器会有优化:将堆分配转化为栈分配。如果一个对象在子程序中被分配,要使指向该对象的指针永远不会逃逸,对象可能是栈分配的候选,而不是堆分配。而栈分配的下一步会是:标量替换


  static void allocate() {    MyObject myObject = new MyObject(2019, 2019.0);  }
复制代码


替换


static void allocate() {    int a = 2019;    double b = 2019.0;}
复制代码


总:1.对象不会逃逸出方法 2.然后进行标量替换 完成栈分配!!!


<hr style=" border:solid; width:100px; height:1px;" color=#000000 size=1">

7.split 分割 字符串(分隔符如:* ^ : | , .) 及注意点

  • split 表达式,其实就是一个正则表达式。【 * ^ | 】等符号在正则表达式中属于一种有特殊含义的字符,如果使用此种字符作为分隔符,必须使用转义符即【 \ 】加以转义。


  String address=”上海|上海市|闵行区|吴中路”;  String[] splitAddress=address.split(“\\|”); //如果以竖线为分隔符,则split的时候需要加上两个斜杠【\\】进行转义  System.out.println(splitAddress[0]+splitAddress[1]+splitAddress[2]+splitAddress[3]);        String address=”上海*上海市*闵行区*吴中路”;  String[] splitAddress=address.split(“\\*”);  System.out.println(splitAddress[0]+splitAddress[1]+splitAddress[2]+splitAddress[3]);        String address=”上海:上海市:闵行区:吴中路”;  String[] splitAddress=address.split(“\\:”);  System.out.println(splitAddress[0]+splitAddress[1]+splitAddress[2]+splitAddress[3]);    String address=”上海.上海市.闵行区.吴中路”;  String[] splitAddress=address.split(“\\.”);  System.out.println(splitAddress[0]+splitAddress[1]+splitAddress[2]+splitAddress[3]);        String address=”上海^上海市^闵行区^吴中路”;  String[] splitAddress=address.split(“\\^”);  System.out.println(splitAddress[0]+splitAddress[1]+splitAddress[2]+splitAddress[3]);        String address=”上海@上海市@闵行区@吴中路”;  String[] splitAddress=address.split(“@”);  System.out.println(splitAddress[0]+splitAddress[1]+splitAddress[2]+splitAddress[3]);        String address=”上海,上海市,闵行区,吴中路”;  String[] splitAddress=address.split(“,”);  System.out.println(splitAddress[0]+splitAddress[1]+splitAddress[2]+splitAddress[3]);
复制代码


  • 如果使用多个分隔符则需要借助 | 符号,但需要转义符的仍然要加上分隔符进行处理。


  String address=”上海^上海市@闵行区#吴中路”;  String[] splitAddress=address.split(“\\^|@|#”);  System.out.println(splitAddress[0]+splitAddress[1]+splitAddress[2]+splitAddress[3]);
复制代码


参考文章:https://blog.csdn.net/w372426096/article/details/80333657https://segmentfault.com/a/1190000022778791https://blog.csdn.net/u010002184/article/details/81347559https://www.cnblogs.com/latter/p/5665015.htmlhttps://www.cnblogs.com/that-jay/p/13280995.html

-----------------------java 基础--------------------------

1.一次编译,多次运行

  • javac hello.java 生成.class 字节码文件

  • 加载到 JVM 中

  • 1.执行引擎进行解释执行,编译成机器码

  • 2.执行引擎进行 JIT(编译执行),编译成机器码

  • 3.上面用哪个,看虚拟机的心情

  • 4.解释和编译共存:

  • 解释执行有个缺点就是每次都要解释字节码然后才生成机器码让计算机执行,解释的过程会占用很多的时间。于是 JVM 中诞生了编译器,编译器可以通过热点代码探测技术,找到运行次数最多的代码,把这些代码及时编译为机器码,在下次调用这些代码时跳过解释的步骤,直接执行编译好的机器码,以达到加速运行时间的目的。



  • 为什么跨平台?

  • 不同的平台的解释器是不同,但是 JVM 实现是相同的。


<hr style=" border:solid; width:100px; height:1px;" color=#000000 size=1">

2.JVM

  • 运行时区域:

  • 公有:堆、方法区

  • 私有:虚拟机栈、本地方法栈、程序计数器

  • 执行引擎:

  • JIT(即时编译器)

  • GC

  • 解释器

  • 本地库接口(JNI)

3.JVM 内存区域

  • 99%的垃圾回收;new 的对象会存在这;新生代+老年代;静态变量+字符串常量池+常量池(运行时常量池)(原来在方法区,现在在堆中)

  • -Xms -Xmx

  • OOM:

  • 设置参数:dumpOnOutOfMemory;jConsole;jmap 生成 dump 文件

  • 分析对象快照:jhat、map 两种工具进行分析

  • 方法区

  • 1%的垃圾回收:类的卸载

  • 由永久代到了元空间,也会出现 OOM,但是在元空间使用的是直接存储,所以几率降低了。

  • OOM 主要是加载的类过多

  • 直接内存

  • 不属于运行时区域,但也会频繁使用,引入 NIO 避免 java 堆和 Native 堆之间来回复制数据。

  • 程序计数器

  • 唯一一个不会 OOM 的,其两点作用

  • 1 是对于线程的顺序执行;2 是线程切换时的上下文保护

  • java 虚拟机栈

  • -Xss

  • 主要是一个个的栈帧(方法),包含局部变量表、操作数、动态链接、方法出口

  • 会出现栈溢出也会出现 OOM,在刚开始无法申请到空间的时候。

  • java 本地方法栈

  • 同 java 虚拟机栈


<hr style=" border:solid; width:100px; height:1px;" color=#000000 size=1">

----------------------java 调优-------------------------

1.java 占用 cpu 过高怎么排查?

  • top,查看进程 pid

  • top -Hp pid 找到线程(99%的占用量)

  • jstack 进程 id>thread.log

  • 分析 thread.log 文件,查看每个线程的状态、名称等等,找到上面的线程进行分析哪行代码消耗 cpu。

2.OOM 的排查?

  • 堆,可以设置参数 -Xms -Xmx 来设置大小

  • 方法区,可以设置参数 -XX:PermSize =64m -XX:MaxPermSize = 256m 来设置大小

  • 元空间,可以设置参数 -XX:MetaspaceSize

  • 栈溢出 ,可以设置参数 -Xss

  • 分析:1)生成 dump 文件(对象内存快照)-XX:+HeapDumpOnOutOfMemoryError 生成 dump 文件 jmap -dump:fotmat=b,file = -/heap.hprof 进程号 Jconsole 工具生成 dump 文件 2)分析 dump 文件 jhat 工具 mat 工具<hr style=" border:solid; width:100px; height:1px;" color=#000000 size=1">

发布于: 刚刚阅读数: 7
用户头像

Studying_swz

关注

还未添加个人签名 2020-12-23 加入

还未添加个人简介

评论

发布
暂无评论
java部分基础总结_java 编程_Studying_swz_InfoQ写作社区