• Java基础


    1、Java面向对象三大特性:封装,继承,多态

    封装:

    • 将现实中的客观事物封装成抽象的类。
    • 对一个类中的变量,方法进行访问符修饰,以达到有些变量,方法对外开放,有些变量,方法隐藏。
    • 针对第2点对应的访问修饰符有(范围从大到小):public > protected > default > private。
    • 由于封装隐藏了具体实现,仅提供接口供外部调用,所以在一定程度上可以提高安全性。
    • 示例代码如下:
     1 package com.spring.test.vo;
     2 
     3 /**
     4  * @Author: philosopherZB
     5  * @Date: 2019/9/28
     6  * 将现实中客观存在的人,抽象成一个封装类Person
     7  */
     8 public class Person {
     9     //使用private隐藏name属性变量,仅提供get,set方法给外部调用以达到修改的目的
    10     private String name;
    11 
    12     public String getName() {
    13         return name;
    14     }
    15 
    16     public void setName(String name) {
    17         this.name = name;
    18     }
    19 }
    View Code

    继承:

    • 可以实现已存在的类的变量,方法(非private),并可以扩展单属于自我的变量及方法。
    • 继承在Java中为单继承,即一个子类只能继承一个父类(可以实现多个接口)。
    • 通过继承创建的类称为“子类”,“派生类”。
    • 被继承的类称为“父类”,“基类”,“超类”。
    • 继承的关系是is-a,比如说老师是人,狮子是动物等。
    • 继承可以降低代码的重复性,方便后续对公共行为的维护,不过同时也增加了代码的耦合度。
    • 子类不继承父类的构造器,而是显示或隐式的调用(如果父类中存在不带参构造器,则子类隐式调用该构造器;否则如果父类中仅存在带参构造器,则子类需要通过super来显示调用父类带参构造器)。
    • 示例代码如下:
     1 package com.spring.test.service;
     2 
     3 /**
     4  * @Author: philosopherZB
     5  * @Date: 2019/9/29
     6  */
     7 public class Test {
     8     public static void main(String[] args){
     9         Teacher teacher = new Teacher("老师");
    10         Doctor doctor = new Doctor("医生");
    11         teacher.eat();
    12         teacher.sleep();
    13         doctor.eat();
    14         doctor.sleep();
    15     }
    16 }
    17 
    18 //将现实中客观存在的人,抽象成一个封装类Person
    19 class Person {
    20     private String name;
    21 
    22     public Person(String name){
    23         this.name = name;
    24     }
    25     public void eat(){
    26         System.out.println(name+"吃饭");
    27     }
    28     public void sleep(){
    29         System.out.println(name+"睡觉");
    30     }
    31 }
    32 
    33 //老师是一个人,继承父类Person
    34 class Teacher extends Person{
    35     //显示调用父类带参构造器
    36     public Teacher(String name) {
    37         super(name);
    38     }
    39 }
    40 
    41 //医生是一个人,继承父类Person
    42 class Doctor extends Person{
    43     //显示调用父类带参构造器
    44     public Doctor(String name) {
    45         super(name);
    46     }
    47 }
    View Code

    多态:

    • 对同一个行为体现出不同的表现形式,比如吃,可以吃饭,吃零食等。
    • 在Java中体现为对方法的重写以及重载,即传入参数来决定做哪一种具体的动作(重载)不同类之间同一方法不同表现(重写)。
    • 重写一般为父子类之间对方法的不同操作,重载一般为同一个类中对方法的不同操作。
    • 示例代码如下:
     1 package com.spring.test.service;
     2 
     3 /**
     4  * @Author: philosopherZB
     5  * @Date: 2019/9/29
     6  */
     7 public class Test {
     8     public static void main(String[] args){
     9         Teacher teacher = new Teacher("老师");
    10         teacher.eat();
    11         teacher.eat("零食");
    12     }
    13 }
    14 
    15 //将现实中客观存在的人,抽象成一个封装类Person
    16 class Person {
    17     private String name;
    18 
    19     public Person(String name){
    20         this.name = name;
    21     }
    22     public void eat(){
    23         System.out.println(name+"吃饭");
    24     }
    25     //同一类中对方法进行重载
    26     public void eat(String s){
    27         System.out.println("重载吃"+s);
    28     }
    29 }
    30 
    31 //老师是一个人,继承父类Person
    32 class Teacher extends Person{
    33     //显示调用父类带参构造器
    34     public Teacher(String name) {
    35         super(name);
    36     }
    37     //父子类之间对方法进行重写
    38     @Override
    39     public void eat(){
    40         System.out.println("重写吃饭");
    41     }
    42 }
    View Code

    2、关键字:final

    final:

    • 修饰类,该类将不能被继承,其中的方法也会被隐式的指定为final方法。
    • 修饰方法,该方法将不能被重写。可以提高安全性,在早期的Java版本中可以提高一些效率(由于内嵌调用),不过如今private可以将方法隐式的指定为final。
    • 修饰变量,如果是基本数据类型的变量,则其数值一旦在初始化之后便不能更改;如果是引用类型的变量,则在对其初始化之后便不能再让其指向另一个对象。

    3、基本数据类型

    基本数据类型(四个整数,两个浮点,一个字符,一个布尔):

    • byte,占8位,1字节,默认值:0,包装类:java.lang.Byte
    • short,占16位,2字节,默认值:0,包装类:java.lang.Short
    • int,占32位,4字节,默认值:0,包装类:java.lang.Integer
    • long,占64位,8字节,默认值:0L,包装类:java.lang.Long
    • float,占32位,4字节,默认值:0.0f,包装类:java.lang.Float
    • double,占64位,8字节,默认值:0.fd,包装类:java.lang.Double
    • char,可以存储任何字符,包装类:java.lang.Character
    • boolean,默认值:false,包装类:java.lang.Boolean

    4、JDK和JRE

    JDK(Java Development Kit):

    • Java开发工具包,提供了Java开发环境以及运行环境(一般安装该环境,因为JDK包含了JRE)。
    • 如果web程序中有使用到JSP,那么JDK还可以将JSP转换之后的Servlet进行编译。 

    JRE(Java Runtime Environment):

    • Java运行环境,为Java的运行提供了必要的环境支持。 

    5、Math三个常用方法

    Math三个常用方法:

    • Math.ceil(),向上取整
    • Math.floor(),向下取整
    • Math.round(),此方法并非四舍五入,而是加0.5之后向下取整
    • 示例代码如下:
    1 public class Test {
    2     public static void main(String[] args){
    3         System.out.println(Math.ceil(1.5));//向上取整,输出2.0
    4         System.out.println(Math.floor(1.3));//向下取整,输出1.0
    5         System.out.println(Math.round(-1.5));//加0.5之后向下取整,输出-1(为了加深理解可以试一下传入-1.6,看看输出什么)
    6     }
    7 }
    View Code

    6、操作字符串:String,StringBuilder,StringBuffer

    String:

    • 不可变的对象(也可以称之为常量),不可变的原因是String源码中用了 private final char value[];

    StringBuilder:

    • 可变的对象,可变的原因是StringBuilder继承自AbstractStringBuilder,而AbstractStringBuilder源码中用了 char value[];
    • 线程不安全,不过效率比较高

    StringBuffer:

    • 可变的对象,可变的原因是StringBuffer继承自AbstractStringBuilder,而AbstractStringBuilder源码中用了 char value[];
    • 线程安全,相比StringBuilder,其内部方法使用了synchronized关键字,保证线程安全。

    7、String s = "Test" 和 String s = new String("Test")

    String s = "Test":

    • 此操作首先会去常量池检查是否存在"Test",如果存在,则直接在栈中创建一个s指向常量池中"Test"的地址(引用);如果不存在,则先在常量池中创建一个"Test",随后再在栈中创建一个s指向该"Test"的地址(引用)。
    • 对于第一点中提出的常量池一般是属于运行时数据区中的方法区,而栈指的是运行时数据区中的虚拟机栈。

    String s = new String("Test"):

    • 此操作首先会去常量池检查是否存在"Test",如果存在,则到堆中直接创建一个"Test",再在栈中创建一个s指向堆中"Test"的地址(引用);如果不存在,则先在常量池中创建一个"Test",随后到堆中创建一个"Test",最后再在栈中创建一个s指向堆中"Test"的地址(引用)。
    • 对于第一点中提出的常量池一般是属于运行时数据区中的方法区,而栈指的是运行时数据区中的虚拟机栈,堆指的是运行时数据区中的堆。
    • new操作每次都会在堆中创建一个新对象。

    总结:

    • 对于常量池而言,始终都只存在一个相同的"Test",即多个String用==比较都会返回true,因为地址(引用)一样。
    • 对于堆而言,每次操作都会创建一个新的对象,即多个new String用==比较都会返回false,因为地址(引用)不一样。
    • 示例代码如下:
     1 public class Test {
     2     public static void main(String[] args){
     3         String s1 = "Test";
     4         String s2 = "Test";
     5         String s3 = new String("Test");
     6         String s4 = new String("Test");
     7         System.out.println(s1==s2);//true
     8         System.out.println(s3==s4);//false
     9     }
    10 }
    View Code

    8、值传递,引用传递

    值传递:

    • 值传递不会改变实际参数的内容。
    • 基本类型是值传递,String以及基本类型包装类同样是值传递。

    引用传递:

    • 引用传递会改变实际参数的内容。
    • 引用传递不会改变实际参数的参考地址。

    总结:

    • 如果创建的内容保存在了常量池中,那么是值传递;如果保存在了堆中,则是引用传递(个人理解)。
    • 示例代码如下:
     1 public class Test {
     2     public static void main(String[] args){
     3         //值传递
     4         String s1 = "Test--1";
     5         change(s1);
     6         System.out.println(s1);//输出Test--1
     7         //引用传递
     8         StringBuilder sb = new StringBuilder("str--1");
     9         change2(sb);
    10         System.out.println(sb);//输出str--1--2
    11     }
    12     private static void change(String str){
    13         str = "Test--2";
    14     }
    15     private static void change2(StringBuilder str){
    16         str.append("--2");
    17     }
    18 }
    View Code

    9、= = 和 equels

    = =:

    • 对于基本类型,基本类型包装类,String而言是比较值是否相等(其实本质上还是比较的引用是否相等,因为对于基本类型而言,他所创建的值是存在了常量池,而常量池不允许出现重复的值,对应栈中指向常量池的地址(引用)都是同一个(个人理解))。
    • 对于引用类型,比较的是引用是否相等。

    equels:

    • 其底层是用= =来实现的,本质上仍然是比较地址(引用)是否相等。
    • 大部分类都会自己重写equels方法,实现能够比较值是否相等,如String,Integer等。

    关于Integer t1 = 128,Integer t2 = 128,t1==t2,返回false原因

    • Integer会缓存[-128,127]中的数,如果在这之中,则会返回true,否则,将会在堆中重新创建新的对象,结果自然返回false。
    • 示例代码如下:
     1 public class Test {
     2     public static void main(String[] args){
     3         Integer t1 = 127;
     4         Integer t2 = 127;
     5         Integer t3 = 128;
     6         Integer t4 = 128;
     7         System.out.println(t1==t2);  //true
     8         System.out.println(t3==t4);  //false
     9     }
    10 }
    11 //以下结果来自反编译,可以看到使用的是Integer.valueOf()方法进行自动装箱
    12 public class Test
    13 {
    14     public static void main(String[] args) {
    15         Integer t1 = Integer.valueOf(128);
    16         Integer t2 = Integer.valueOf(128);
    17         System.out.println((t1 == t2));
    18     }
    19 }
    20 
    21 //以下源码来自于jdk8中的Integer类
    22 public static Integer valueOf(int i) {
    23     //如果在-128到127之间,则直接返回缓存中的数值,否则创建一个新的对象保存
    24     if (i >= IntegerCache.low && i <= IntegerCache.high)
    25         return IntegerCache.cache[i + (-IntegerCache.low)];
    26     return new Integer(i);
    27 }
    28 
    29 private static class IntegerCache {
    30     static final int low = -128;   //最小值为-128
    31     static final int high;         //最大值
    32     static final Integer cache[];  //缓存存储数组
    33 
    34     static {
    35         // high value may be configured by property--最大值可以通过属性配置
    36         int h = 127;    //最大值127
    37         String integerCacheHighPropValue =
    38                 sun.misc.VM.getSavedProperty("java.lang.Integer.IntegerCache.high");
    39         if (integerCacheHighPropValue != null) {
    40             try {
    41                 int i = parseInt(integerCacheHighPropValue);
    42                 i = Math.max(i, 127);
    43                 // Maximum array size is Integer.MAX_VALUE
    44                 h = Math.min(i, Integer.MAX_VALUE - (-low) -1);
    45             } catch( NumberFormatException nfe) {
    46                 // If the property cannot be parsed into an int, ignore it.
    47             }
    48         }
    49         high = h;
    50         
    51         cache = new Integer[(high - low) + 1];  //缓存数组大小为256
    52         int j = low;   //得到最小值-128
    53         //从-128开始循环递增把对应的数值加入到缓存数组中
    54         for(int k = 0; k < cache.length; k++)
    55             cache[k] = new Integer(j++);
    56 
    57         // range [-128, 127] must be interned (JLS7 5.1.7)
    58         //校验缓存最大值是否大于127
    59         assert IntegerCache.high >= 127;
    60     }
    61 
    62     private IntegerCache() {}
    63 }
    View Code

    关于hashCode 和 equels:

    • equels返回true,则hashCode必然相等。
    • hashCode相等,equels却不一定返回true(因为存在hash碰撞)。

    10、接口,抽象类

    接口:

    • 接口中的方法对应的默认访问修饰符为:public
    • 对于接口的实现关键字为:interface,其他类需要通过:implements来实现接口
    • 接口不允许有非抽象方法的实现(jdk8支持静态方法,可以直接通过接口名.方法名调用)。

    抽象类:

    • 抽象类中的方法对应的访问修饰符是任意的。
    • 对于抽象类的实现关键字为:abstract,其他类需要通过:extends来继承抽象类
    • 抽象类允许有非抽象方法的实现。
    • 抽象类有构造方法,也有main方法,并且可以直接运行。

    11、面向对象六大基本原则

    单一职责原则(Single Responsibility Priciple):

    • 对于一个类而言,应该只有一个引起他变化的原因。
    • 比如,不要将teacher,doctor都放在一个person类中,而是将其拆成teacher类,doctor类。
    • 能够一定程度上降低耦合度。

    开闭原则(Open Close Principle):

    • 对于扩展是开放的,对于修改是关闭的。
    • 一般来说就是不要修改之前的代码,可以选择继承或者实现接口来扩展对应的功能。

    里式替换原则(Liskov Substitution Principle):

    • 所有引用父类的地方都可以透明的使用其子类对象。
    • 即父类出现的地方,把父类换成子类不会出现异常或错误;反之,子类出现的地方,把子类换成父类可能会出现异常或错误。
    • 比如,person类具有eat功能,那么将person.eat换成teacher.eat是没有问题的;而如果teacher类具有独特的teach功能,那么如果将teacher.teach换成person.teach就会出现错误。

    依赖倒置原则(Dependence Inversion Principle):

    • 高层模块不依赖低层模块,而是应该依赖其抽象
    • 抽象不依赖于细节,细节依赖于抽象
    • 比如teacher类中应该尽量做一些老师共有的抽象行为,比如教书,这样后面的语文老师,数学老师等可以继承该抽象老师类进行扩展。

    接口隔离原则(Interface Segregation Principle):

    • 户端不应该依赖它不需要的接口;一个类对另一个类的依赖应该建立在最小的接口上
    • 尽量一个接口只实现一个功能,如果接口功能太多可进行拆分。

    迪米特原则(Law of Demeter or Least Knowlegde Principle):

    • 一个对象应该对其他对象有最少的了解。
    • 此原则与接口隔离原则可以结合使用,尽量保持一个方法一个具体行为,而不要涵盖多个行为。

    (以上所有内容皆为个人笔记,如有错误之处还望指正。)

  • 相关阅读:
    二维码生成:使用 JavaScript 库QRCode.js生成二维码
    VC++6.0远程调试(亲试成功)
    Linux同时安装python2和Python3
    Python打包-py2exe
    camera按键采集图像及waitKey的用法(转)
    ucos实时操作系统学习笔记——任务间通信(信号量)(转)
    STM32硬件IIC驱动设计(转)
    基于STM32F4移植W5500官方驱动库ioLibrary_Driver(转)
    USB基础知识概论(版本:v0.9.2)
    usb帧格式
  • 原文地址:https://www.cnblogs.com/xihuantingfeng/p/11604790.html
Copyright © 2020-2023  润新知