[Java]基础语法和面向对象

Posted Spring-_-Bear

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[Java]基础语法和面向对象相关的知识,希望对你有一定的参考价值。

一、基础语法

0. Java 特点及应用领域

  1. Java 是基于 JVM 虚拟机的跨平台语言,一次编写到处运行
  2. Java 程序易于编写且有垃圾回收机制,不必考虑内存管理
  3. Java 虚拟机拥有工业级的稳定性和高度优化的性能,经受了长时间的考验
  4. Java 拥有最广泛的开源社区支持,各种高质量组件随时可用
  5. 互联网和企业应用,这是 Java EE 的长期优势和市场地位、大数据平台,主要有 Hadoop、Spark、Flink 等、droid 移动平台

1. JDK、JVM 与 JRE 之间的关系

  1. JVM 是一个虚拟的计算机,具有指令集并使用不同存储区域。负责执行指令,管理数据、内存、寄存器,包含在 JDK 中
  2. JDK = JRE + Java 开发工具(java、javac、javap、javadoc ······)
  3. JRE = JVM + Java 核心类库
  4. JSR(Java Specification Request):规范
  5. JCP(Java Community Process):组织
  6. RI(Reference Implementation):基于 JSR 的参考实现
  7. TCK(Technology Compatibility Kit):兼容性测试套件
  8. JAVA_HOME - D:\\Java\\jdk1.8.0_131
  9. Path - %JAVA_HOME%\\bin
  10. CLASSPATH - .;%JAVA_HOME%\\lib\\dt.jar;%JAVA_HOME%\\lib\\tools.jar;
  11. jdb.exe:Java调试器,用于开发阶段的运行调试

2. 变量类型数据范围

  1. Java 数据类型中没有无符号类型
  2. 浮点数组成:符号位 + 指数位 + 尾数位
  3. Java 浮点类型默认为 double 类型,定义 float 类型时需要在末尾加上 F 或 f
  4. Java语言对布尔类型的存储并没有做规定,因为理论上存储布尔类型只需要 1 bit,但是通常 JVM 内部会把 boolean 表示为4字节整数
类型字节数范围
byte1-128 - 127
short2-32768 - 32767
int4-231 - 231-1
long8-263 - 263-1
float4-3.403E38 - 3.403E38
double8-1.798E308 - 1.798E308
char2ISO 单一字符集
boolean1true or false

3. 字符串与常用编码

  1. Java 在内存中总是使用 Unicode 表示字符,所以,一个英文字符和一个中文字符都用一个 char 类型表示,它们都占用两个字节
  2. Java 13开始,字符串可以用"""..."""表示多行字符串。多行字符串的排版不规则,则总是以最短的行首空格为基准
  3. 注意要区分空值 null 和空字符串 “”,空字符串是一个有效的字符串对象,它不等于 null
  4. 使用 System.out.printf("%%"); 输出 %
类型字符占用字节数汉字占用字节数
ASCII1
Unicode22
UTF - 813
GBK12

4. 类型转换

  1. 自动类型转换一:char -> int -> long -> float -> double
  2. 自动类型转换二:byte -> short -> int -> long -> float -> double
  3. (byte,short) 和 char 之间不会自动类型转换
  4. byte、char、short 在进行运算时,当作 int 处理
  5. char 类型可以保存 int 类型的常量值,但不能保存 int 型的变量值,需要强转
  6. 浮点数运算和整数运算相比,只能进行加减乘除这些数值计算,不能做位运算和移位运算
  7. 整数运算在除数为 0 时会报错,而浮点数运算在除数为 0 时,不会报错,但会返回以下几个特殊值(NaN:Not a Number、Infinity:无穷大、-Infinity:负无穷大)
  8. 如果强制类型转换后超过了整型能表示的最大范围,将返回整型的最大值
  9. 如果要进行四舍五入,可以对浮点数加上 0.5 再强制转型

5. 运算符

  1. 自增自减运算符的注意事项

    int i = 1;
    i = i++;
    // output:1
    System.out.println(i);
    
    int i = 1;
    i = ++i;
    // output:2
    System.out.println(i);
    
  2. 三元运算符的使用细节

    Object object = true ? new Integer(1) : new Double(2.0);
    // 三元运算符需要看作一个整体,故输出 1.0;if - else 分支结构则输出 1
    System.out.println(object);
    

6. 运算符优先级

运算顺序操作符
. () ; ,
R -> L++ – ~ !
L -> R* / %
L -> R+ -
L -> R<< >> >>>
L -> R< > <= >= instanceof
L -> R== !=
L -> R&
L -> R^
L -> R|
L -> R&&
L -> R||
L -> R? :
R -> L= *= /= %=
R -> L+= -= <<= >>=
R -> L>>>= &= ^= |=

7. 保留字

  1. strictfp:strict float point (精确浮点) ,strictfp关键字可应用于类、接口、方法。 使用 strictfp 关键字声明一个方法时,该方法中所有的 float 和 double 表达式都严格遵守 FP-strict 的限制,符合 IEEE-754 规范
  2. volatile 是 jvm 提供的轻量级同步机制。作用是:、 保证可见性、禁止指令重排、不保证原子性

8. 位运算

  1. 算术右移(>>) :低位丢弃,符号位不变,高位补符号位
  2. 算术左移(<<):符号位不变,低位补0
  3. 无符号右移(>>):低位丢弃,高位补 0

9. switch

  1. switch 的 case 后跟常量或常量表达式,switch 中的表达式只能是以下类型中的一种:byte、short、char、int、enum、String
  2. 从 Java 12开始,switch 语句升级为更简洁的表达式语法,使用类似模式匹配(Pattern Matching)的方法,保证只有一种路径会被执行,并且不需要 break 语句
  3. 大多数时候,在 switch 表达式内部,我们会返回简单的值。但是,如果需要复杂的语句,我们也可以写很多语句,放到 … 里,然后,用 yield 返回一个值作为 switch 语句的返回值
public class Main 
    public static void main(String[] args) 
        String fruit = "orange";
        int opt = switch (fruit) 
            case "apple" -> 1;
            case "pear", "mango" -> 2;
            default -> 
                int code = fruit.hashCode();
                yield code; // switch语句返回值
            
        ;
        System.out.println("opt = " + opt);
    

10. 数组

  1. 数组是引用类型,创建数组时使用了 new 关键字

  2. 数组可以使用静态方式进行初始化

    int[] array = 2,54,3,23;
    
  3. 创建数组后,若没有赋值,则有默认值

    int[] a = new int[3];
    // output: [0, 0, 0]
    System.out.println(Arrays.toString(a));
    
  4. Java 中二维数组每行的元素个数可以不一致

    int[][] array = new int[3][];
    array[0] = new int[3];
    array[1] = new int[2];
    array[2] = new int[6];
    
  5. 遍历多维数组

    int[][] array =new int[][]
        1, 2, 3,
        2, 3,
        54, 234, 3, 131, 34
    ;
    System.out.println(Arrays.deepToString(array));
    

11. 内部排序和外部排序

  1. 内部排序:将需要处理的所有数据都加载到内部存储器中进行排序(交换式排序法、选择式排序法和插入式排序法)
  2. 外部排序:数据量过大,无法全部加载到内存中,需要借助外部存储进行排序(合并排序法和直接合并排序法)

12. 内存布局

  1. 栈:一般存放基本数据类型(局部变量)
  2. 堆:存放对象(数组)
  3. 方法区:常量池(字符串)、类加载信息

13. 方法重载与可变参数

  1. 方法重载方法签名必须不同,返回值不是方法签名的一部分
  2. 可变参数的实参可以是 0 个或是多个,其本质就是数组
  3. 形参列表中可以同时有普通形参和可变参数,但必须保证可变参数是形参列表的最后一个参数
  4. 一个方法的形参列表只能出现一个可变参数

14. 作用域与构造器

  1. 类的属性有默认值,局部变量没有默认值
  2. 构造器完成对象的初始化,并不是创建对象;调用构造器时堆中已经分配了对象的空间,构造器只是负责对对象进行初始化
  3. 没有在构造方法中初始化字段时,引用类型的字段默认是 null,数值类型的字段用默认值,int类型默认值是 0,布尔类型默认值是 false

15. 对象创建流程和 this 关键字

  1. 加载类信息,生成 .class 对象
  2. 在堆中为对象分配空间
  3. 完成对象的默认初始化
  4. 构造器完成对象的初始化
  5. JVM 会给每个对象分配 this,代表当前对象;哪个对象调用,this 就指向它
  6. this 不能在类定义的外部使用,只能在类定义的方法中使用

二、面向对象

1. 继承内存布局

  1. 子类继承了父类所有的方法和属性,但父类的私有属性不能在子类中直接访问
  2. 当创建子类的对象时,无论使用子类的哪个构造器,默认情况下总会去调用父类的无参构造器,如果父类没有提供无参构造器,则必须在子类中使用 super 去显式调用父类的构造器完成父类的初始化工作,否则编译不会通过
  3. super() 和 this() 都只能放在构造器的第一行,因此这两个方法不能同时存在一个构造器中
  4. 从 Java 15开始,允许使用 sealed 关键字修饰 class,并通过 permits 明确写出能够从该 class 继承的子类名称
  5. instanceof 实际上判断一个变量所指向的实例是否是指定类型,或者这个类型的子类
  6. 从 Java 14 开始,判断 instanceof 后,可以直接转型为指定变量,避免再次强制转型

2. Object 类的所有方法

3. super 和 this 的区别

  1. super 代表父类的引用,用于访问父类的属性、方法、构造器
  2. 使用 super 不能访问父类的 private 成员
  3. this 关键字可以用户调用本类的构造器以及执行调用的对象
区别点thissuper
方法访问本类属性,没有则依次查找父类直接依次查找父类属性
属性访问本类方法,没有则依次查找父类直接依次查找父类方法
构造器行首调用本类其它构造器行首调用父类指定构造器
特殊表示当前对象表示子类中访问父类的对象

4. 方法重写与重载

  1. 子类重写父类的方法时,返回类型可以是父类返回类型的子类
  2. 子类不能缩小父类方法的访问权限
  3. 重载与重写也体现了多态

5. 动态绑定机制

  1. 当调用对象方法的时候,该方法会和该对象的内存地址 / 运行类型动态绑定
  2. 当调用对象属性时,不存在动态绑定,在哪儿声明在哪使用
  3. 多态是指,针对某个类型的方法调用,其真正执行的方法取决于运行时期实际类型的方法

6. == 和 equals()

作用
==判断基本类型时判断值是否相等;判断引用类型时判断是否引用到同一个对象
equals()只能判断引用类型是否引用到同一个对象,子类往往重写

7. Object 类的 toString()、equas() 方法

public String toString() 
        return getClass().getName() + "@" + Integer.toHexString(hashCode());

public boolean equals(Object obj) 
        return (this == obj);

8. 类变量

  1. 类变量也即静态变量,类方法也即静态方法
  2. JDK 8 以前类变量放在方法区的静态域中,JDK 8 以后放在堆的对应类的 class 对象尾部
  3. 当方法中不设计到与任何对象相关的成员时,则可以将方法设计为静态方法以提高运行效率
  4. 静态代码块、静态方法只能调用静态成员;普通方法和普通代码块可以调用任何成员
  5. 因为静态方法属于 class 而不属于实例,因此,静态方法内部,无法访问 this 变量,也无法访问实例字段,它只能访问静态字段

9. 代码块和类加载

  1. 代码块可以理解为只有方法体的方法,它没有方法名、返回值、参数,不能通过对象或类显式调用,只有在加载类或是创建对象时隐式调用
  2. 代码块的修饰符只能是 static 或无修饰符,方法体中的语句可以是任何正确逻辑语句
  3. 类代码块的调用顺序优先于构造器
  4. 类加载时机:创建该类的对象时、创建子类的对象时父类也会加载、使用类的静态成员时
  5. 如果只是调用类的静态成员,普通代码块并不会执行,普通代码块可以看作是构造器的一种补充机制,所以在调用类的静态成员但没有创建类的对象时,构造器并未被调用,因而类的普通代码块不会被执行
class GirlFriend 
   public static final int AGE = 2;
   // 当使用到 GirlFriend.AGE 时,此静态代码块不会执行即此类未加载
   static 
       System.out.println("I am your only girlfriend");
   

10. Java 代码执行顺序

  1. 父类的静态代码块和静态属性按定义顺序依次执行
  2. 子类的静态代码块和静态属性按定义顺序依次执行
  3. 父类的普通代码块与普通属性按定义顺序依次执行
  4. 父类的构造器
  5. 子类的普通代码块与普通属性按定义顺序依次执行
  6. 子类的构造器

11. 单例设计模式

  1. 设计模式是在大量的实践中总结和理论化之后优选的代码结构、编程风格以及解决问题的思考方式。设计模式好比经典的棋谱,不同的棋局采用不同的棋谱,免去再次思考和摸索的过程
  2. 类的单例设计模式:采取一定的方法在整个软件系统中,某个类有且仅有一个对象实例,并且该类只提供一个取得该对象的方法
  3. 单例模式的实现:构造器私有化、在类的内部创建对象、向外部提供一个静态的公共方法取得该对象
  4. 饿汉式与懒汉式的区别:创建时机不同,饿汉式在类加载是就创建了对象而懒汉式在使用到对象时才创建
  5. 饿汉式不存在线程安全问题,懒汉式存在线程安全问题
// 单例设计模式饿汉式
class GirlFriend 
   private GirlFriend() 
   private static final GirlFriend girlFriend = new GirlFriend();
   public static GirlFriend getGirlFriend() 
       return girlFriend;
   


// 单例设计模式懒汉式
class GirlFriend 
   private GirlFriend() 
   private static GirlFriend girlFriend;
   public static GirlFriend getGirlFriend() 
       if (girlFriend == null) 
           girlFriend = new GirlFriend();
       
       return girlFriend;
   

  1. java.lang.Runtime 类就是经典的懒汉式单例模式
public class Runtime 
   private static Runtime currentRuntime = new Runtime();

   /**
    * Returns the runtime object associated with the current Java application.
    * Most of the methods of class <code>Runtime</code> are instance
    * methods and must be invoked with respect to the current runtime object.
    *
    * @return  the <code>Runtime</code> object associated with the current
    *          Java application.
    */
   public static Runtime getRuntime() 
       return currentRuntime;
   

   /** Don't let anyone else instantiate this class */
   private Runtime() 

12. final

  1. final 的使用场景:不希望某个类被继承、不希望父类的方法被子类重写、不希望类的成员值被修改、不希望局部变量的值被修改
  2. final 修饰的属性必须初始化,此后不能更改,初始化可在三个地方进行:定义时初始化、构造器中初始化、代码块中初始化
  3. 如果 final 修饰的属性同时是 static 的,那么只能在两个地方初始化:定义时初始化、静态代码块中初始化
  4. 如果非 final 类中含有 final 修饰的方法,则该方法可以被继承不能被重写
  5. 八大包装类都是 final 类型的
  6. final 往往和 static 搭配使用,效率更高,因为底层编译器做了优化处理,不会导致类的加载

13. 抽象类

  1. 当父类的某些方法需要被声明,但又不确定如何实现时,可以将其声明为抽象方法,那么这个类就是抽象类
  2. 有抽象方法的类一定是抽象类,抽象类不一定有抽象方法,abstract 只可以修饰类和方法
  3. 如果一个类继承自抽象类,则必须实现父类的所有抽象方法,除非它也是一个抽象类
  4. 抽象方法不能使用 private、final、static 来修饰,因为这些修饰符与重写相违背

14. 接口

  1. JDK 7 及之前的版本,所有的方法都没有方法体即是抽象方法
  2. JDK 8 及之后的版本,接口类中可以有静态方法、默认方法即接口中的方法可以有具体实现
  3. 抽象类实现接口可以不用实现方法;接口继承接口时可以不用实现方法
  4. 接口中的属性能且仅能是 public static final 修饰符
  5. 继承的价值:解决代码的复用性与可维护性
  6. 接口的价值:设计规范方法,一定程度上实现代码解耦即接口规范性 + 多态
  7. Java 的接口特指 interface 的定义,表示一个接口类型和一组方法签名,而编程接口泛指接口规范,如方法签名,数据格式,网络协议等
interface Study
   default public void english() 
       System.out.println("Study english");
   
   public static void math() 
       System.out.println("Study math");
   

15. 内部类

  1. 局部内部类:局部内部类定义在外部类的局部位置,比如方法或代码块中,并且有类名,有以下几个特点:

    1. 可以直接访问外部类的所有成员,包含私有的
    2. 不可以添加访问修饰符,可以添加 final
    3. 局部内部类可以理解为方法的局部变量
    4. 如果外部类和局部内部类的成员重名时,默认遵循就近原则。如果想访问外部类的成员,可以使用 外部类名.this.成员 的方式去访问
    class Outer 
       private String name = "Spring-_-Bear";
       public void m1() 
           class Inner 
               private String name = "springbear";
               private void test() 
                   // 调用局部内部类的成员
                   System.out.println(name);
                   // 调用外部类的成员
                   System.out.println(Outer.this.name);
               
           
           // 调用局部内部类的方法
           new Inner().test();
       
    
    
  2. 匿名内部类:匿名内部类定义在外部类的局部位置,比如方法中,并且没有类名,同时还是一个对象

    1. 匿名内部类本身既是一个类的定义,同时它本身也是一个对象,因而从语法层面看,它既有类定义类的特征也有创建对象的特征
    2. 只能定义在方法或代码块中,匿名内部类的地位就好比一个没有名字的局部变量
    3. 经典使用场景:将匿名内部类直接当作实参传递,简洁高效
    /**
    * 基于接口的匿名内部类
    * 
    * @author Spring-_-Bear
    * @date 2021-12-12 08:50
    */
    public class C02 
       public static void main(String[] args) 
           // 方式1
           I03 i03 = new I03() 
               @Override
               public void cry() 
                   System.out.println("呜呜呜~~~");
               
           ;
           i03.cry();
           // 匿名内部类的类名:I03
           System.out.println(i03.getClass());
       
       
       // 方式2
       new I03() 
               @Override
               public void cry() 
                   System.out.println("呜呜呜~~~");
               
       .cry();
    
    
    interface I03
       void cry();
    
    
  3. 成员内部类:成员内部类可以理解为类的成员

    /**
    * 成员内部类的使用
    *
    * @author Spring-_-Bear
    * @date 2021-12-12 08:50
    */
    public class C02 
       public static void main(String[] args) 
           new Outer().new Inner().m();
       
    
    
    class Outer 
       private String name = "Spring-_-Bear";
       // 成员内部类
       public class Inner 
           public void m() 
               System.out.println(name);
           
       
    
    
  4. 静态内部类

    1. 静态内部类可以直接访问外部类的所有静态成员,但不能访问非静态成员
    2. 静态内部类可以理解类的静态成员

以上是关于[Java]基础语法和面向对象的主要内容,如果未能解决你的问题,请参考以下文章

Java面向对象(OOP)--内部类(局部内部类)

JavaSE入门学习14:Java面向对象之内部类

大数据必学Java基础(四十六):内部类和面向对象项目实战

Java面向对象之内部类的详解

[Java]基础语法和面向对象

[Java]Java 基础语法和面向对象编程(OOP)