# Learn-Java **Repository Path**: fx25hlab/Learn-Java ## Basic Information - **Project Name**: Learn-Java - **Description**: 跟随廖雪峰的官方网站的Java教程,一步步学习Java,打牢基础知识,打通任督二脉。 - **Primary Language**: Unknown - **License**: Apache-2.0 - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 1 - **Forks**: 1 - **Created**: 2021-09-28 - **Last Updated**: 2025-01-02 ## Categories & Tags **Categories**: Uncategorized **Tags**: learn, Java ## README # Learn Java #### 介绍 学习 Java 的开发,Java 版本 15 #### 软件架构 这个项目是为了学习 Java 而创建的。从 Java 入门到放弃,实例由浅入深,跟着实例开发一步步,你会从入门到熟悉 Java 开发。 JDK: 15.0.2 #### 安装教程 basic 基础 1. Hello 2. 整形 3. 浮点型 4. 布尔型 5. 字符和字符串 6. 数组 流程控制 1. 输入和输出 2. if判断语句 3. switch多重选择语句 4. while循环语句 5. do while循环语句 6. for循环语句 面向对象 1. 方法。 - 用private修饰field,拒绝外部访问。private修饰方法不允许外部调用,内部方法是可以调用private方法的。 - 用public修饰field,可能会破坏封装性。需要使用public修饰方法(method)来让外部代码可以间接修改field。 2. 构造方法 - 构造方法的名称就是类名。 - 构造方法的参数没有限制,在方法内部,也可以编写任意语句。 - 构造方法没有返回值(也没有void),调用构造方法,必须用new操作符。 - 如果一个类没有定义构造方法,编译器会自动为我们生成一个默认构造方法,它没有参数,也没有执行语句。如果我们自定义了一个构造方法,那么,编译器就不再自动创建默认构造方法。如果既要能使用带参数的构造方法,又想保留不带参数的构造方法,那么只能把两个构造方法都定义出来。 - 没有在构造方法中初始化字段时,引用类型的字段默认是null,数值类型的字段用默认值,int类型默认值是0,布尔类型默认值是false。 - 可以定义多个构造方法,在通过new操作符调用的时候,编译器通过构造方法的参数数量、位置和类型自动区分 - 一个构造方法可以调用其他构造方法,这样做的目的是便于代码复用。调用其他构造方法的语法是this(…) 3. 方法重载 - 方法名相同,但各自的参数不同,称为方法重载(Overload)。 - 方法重载的返回值类型通常都是相同的。 - 方法重载的目的是,功能类似的方法使用同一名字,更容易记住,因此,调用起来更简单。 4. 继承 - 继承是面向对象编程中非常强大的一种机制,它首先可以复用代码。 - Java使用extends关键字来实现继承 - Java只允许一个class继承自一个类 - 继承有个特点,就是子类无法访问父类的private字段或者private方法。用protected修饰的字段可以被子类访问。protected关键字可以把字段和方法的访问权限控制在继承树内部 - super关键字表示父类(超类)。 5. 阻止继承 - 正常情况下,只要某个class没有final修饰符,那么任何类都可以从该class继承。 - 从Java 15开始,允许使用sealed修饰class,并通过permits明确写出能够从该class继承的子类名称。例如:public sealed class Shape permits Rect, Circle, Triangle {} 可以用Rect名字继承Shape。例如:public final class Rect extends Shape {...} 6. 向上转型 - 把一个子类类型安全地变为父类类型的赋值,被称为向上转型(upcasting)。 - 继承树是Student > Person > Object,所以,可以把Student类型转型为Person,或者更高层次的Object。 7. 向下转型 - 向下转型很可能会失败。失败的时候,Java虚拟机会报ClassCastException。 - 为了避免向下转型出错,Java提供了instanceof操作符,可以先判断一个实例究竟是不是某种类型。instanceof实际上判断一个变量所指向的实例是否是指定类型,或者这个类型的子类。如果一个引用变量为null,那么对任何instanceof的判断都为false。 - 从Java 14开始,判断instanceof后,可以直接转型为指定变量,避免再次强制转型。 8. 区分继承和组合 - 在使用继承时,我们要注意逻辑一致性。它们是is关系 - 具有has关系不应该使用继承,而是使用组合 9. 多态 - 在继承关系中,子类如果定义了一个与父类方法签名完全相同的方法,被称为覆写(Override)。 - Override和Overload不同的是,如果方法签名不同,就是Overload,Overload方法是一个新方法;如果方法签名相同,并且返回值也相同,就是Override。注意:方法名相同,方法参数相同,但方法返回值不同,也是不同的方法。在Java程序中,出现这种情况,编译器会报错。 @Override不是必需的。 - 继承可以允许子类覆写父类的方法。如果一个父类不允许子类对它的某个方法进行覆写,可以把该方法标记为final。用final修饰的方法不能被Override。 - 10. 抽象类 - 如果父类的方法本身不需要实现任何功能,仅仅是为了定义方法签名,目的是让子类去覆写它,那么,可以把父类的方法声明为抽象方法。 - 使用abstract修饰的类就是抽象类。 11. 接口 - 接口,在抽象类中,抽象方法本质上是定义接口规范。 - 如果一个抽象类没有字段,所有方法全部都是抽象方法,就可以把该抽象类改写为接口:interface。 - 所谓interface,就是比抽象类还要抽象的纯抽象接口,因为它连字段都不能有。 - 因为接口定义的所有方法默认都是public abstract的,所以这两个修饰符不需要写出来(写不写效果都一样)。 - 当一个具体的class去实现一个interface时,需要使用implements关键字。 - 在Java中,一个类只能继承自另一个类,不能从多个类继承。但是,一个类可以实现多个interface - 接口继承: - 一个interface可以继承自另一个interface。interface继承自interface使用extends,它相当于扩展了接口的方法。 - 在接口中,可以定义default方法。实现类可以不必覆写default方法。 - ddefault方法的目的是,当我们需要给接口新增一个方法时,会涉及到修改全部子类。 - 如果新增的是default方法,那么子类就不必全部修改,只需要在需要覆写的地方去覆写新增方法。 - default方法和抽象类的普通方法是有所不同的。因为interface没有字段,default方法无法访问字段,而抽象类的普通方法可以访问实例字段。 12. 静态字段和静态方法 - 用static修饰的字段,称为静态字段:static field。 - 所有实例共享一个静态字段。 - 因为interface是一个纯抽象类,所以它不能定义实例字段。但是,interface是可以有静态字段的,并且静态字段必须为final类型。所以我们可以把interface的public static final这些修饰符都去掉。编译器会自动加上public statc final 13. 内部类 - Nested Class,定义在另一个类的内部,所以称为内部类(Nested Class) - Inner Class,如果一个类定义在另一个类的内部,这个类就是Inner Class。Inner Class 的实例不能单独存在,必须依附于一个Outer Class的实例。 - Anonymous Class在方法内部,通过匿名类(Anonymous Class)来定义 - Static Nested Class,和Inner Class类似,但是使用static修饰,称为静态内部类(Static Nested Class),用static修饰的内部类和Inner Class有很大的不同,它不再依附于Outer的实例,而是一个完全独立的类,因此无法引用Outer.this,但它可以访问Outer的private静态字段和静态方法。如果把StaticNested移到Outer之外,就失去了访问private的权限。 - Java的内部类可分为Inner Class、Anonymous Class和Static Nested Class三种:Inner Class和Anonymous Class本质上是相同的,都必须依附于Outer Class的实例,即隐含地持有Outer.this实例,并拥有Outer Class的private访问权限;Static Nested Class是独立类,但拥有Outer Class的private访问权限。 14. classpath和jar - classpath是JVM用到的一个环境变量,它用来指示JVM如何搜索class。 - 因为Java是编译型语言,源码文件是.java,而编译后的.class文件才是真正可以被JVM执行的字节码。 - classpath就是一组目录的集合,它设置的搜索路径与操作系统相关。 - 在Windows系统上,用;分隔,带空格的目录用""括起来 - 在Linux系统上,用:分隔 - classpath的设定方法有两种 - 在系统环境变量中设置classpath环境变量,不推荐; - 在启动JVM时设置classpath变量,推荐。 - 没有设置系统环境变量,也没有传入-cp参数,那么JVM默认的classpath为.,即当前目录 - 不要把任何Java核心库添加到classpath中!JVM根本不依赖classpath加载核心库! - jar包。 - 把package组织的目录层级,以及各个目录下的所有文件(包括.class文件和其他文件)都打成一个jar文件 - jar包就是zip包。制作了一个zip文件。然后,把后缀从.zip改为.jar,一个jar包就创建成功。 - jar包还可以包含一个特殊的/META-INF/MANIFEST.MF文件,MANIFEST.MF是纯文本,可以指定Main-Class和其它信息。 - JVM会自动读取这个MANIFEST.MF文件,如果存在Main-Class,我们就不必在命令行指定启动的类名,而是用更方便的命令。 - jar包还可以包含其它jar包,这个时候,就需要在MANIFEST.MF文件里配置classpath了。 15. Module 模块 - 从Java 9开始,JDK又引入了模块(Module)。主要是为了解决“依赖”这个问题。 - 从Java 9开始,原有的Java标准库已经由一个单一巨大的rt.jar分拆成了几十个模块,这些模块以.jmod扩展名标识,可以在$JAVA_HOME/jmods目录下找到它们 - 这些.jmod文件每一个都是一个模块,模块名就是文件名。模块之间的依赖关系已经被写入到模块内的module-info.class文件了。所有的模块都直接或间接地依赖java.base模块,只有java.base模块不依赖任何模块,它可以被看作是“根模块”,好比所有的类都是从Object直接或间接继承而来。 - 编写模块 - 首先,创建模块和原有的创建Java项目是完全一样的。其中,bin目录存放编译后的class文件,src目录存放源码,按包名的目录结构存放,仅仅在src目录下多了一个module-info.java这个文件,这就是模块的描述文件。 - module是关键字,后面的hello.world是模块的名称,它的命名规范与包一致。花括号的requires xxx;表示这个模块需要引用的其他模块名。除了java.base可以被自动引入外,这里我们引入了一个java.xml的模块。 - 打包JRE - 使用模块可以按需打包JRE - 访问权限 - 使用模块对类的访问权限有了进一步限制。只有它声明的导出的包,外部代码才被允许访问。 Java核心类 1. String与编码 - 在Java中,String是一个引用类型,它本身也是一个class。实际上字符串在String内部是通过一个char[]数组表示的。 - Java字符串的一个重要特点就是字符串不可变。这种不可变性是通过内部的private final char[]字段,以及没有任何修改char[]的方法实现的。 - 字符串比较,必须使用equals()方法而不能用==。要忽略大小写比较,使用equalsIgnoreCase()方法。 - String类还提供了多种方法来搜索子串、提取子串。常用的方法有: - contains()方法的参数是CharSequence而不是String,因为CharSequence是String的父类 - 去除首尾空白字符,使用trim()方法可以移除字符串首尾空白字符。空白字符包括空格,\t,\r,\n。注意:trim()并没有改变字符串的内容,而是返回了一个新字符串。 - 另一个strip()方法也可以移除字符串首尾空白字符。它和trim()不同的是,类似中文的空格字符\u3000也会被移除。 - String还提供了isEmpty()和isBlank()来判断字符串是否为空和空白字符串。 - 要在字符串中替换子串,有两种方法。一种是根据字符或字符串替换,另一种是通过正则表达式替换 - 要分割字符串,使用split()方法,并且传入的也是正则表达式。 - 拼接字符串使用静态方法join(),它用指定的字符串连接字符串数组。 - 字符串提供了formatted()方法和format()静态方法,可以传入其他参数,替换占位符,然后生成新的字符串。有几个占位符,后面就传入几个参数。参数类型要和占位符一致。我们经常用这个方法来格式化信息。常用的占位符有:%s:显示字符串;%d:显示整数;%x:显示十六进制整数;%f:显示浮点数。 - 要把任意基本类型或引用类型转换为字符串,可以使用静态方法valueOf()。这是一个重载方法,编译器会根据参数自动选择合适的方法。 - 小结:Java字符串String是不可变对象;字符串操作不改变原字符串内容,而是返回新字符串;常用的字符串操作:提取子串、查找、替换、大小写转换等;Java使用Unicode编码表示String和char;转换编码就是将String和byte[]转换,需要指定编码;转换为byte[]时,始终优先考虑UTF-8编码。 2. StringBuilder - Java编译器对String做了特殊处理,使得我们可以直接用+拼接字符串。 - 虽然可以直接拼接字符串,但是,在循环中,每次循环都会创建新的字符串对象,然后扔掉旧的字符串。这样,绝大部分字符串都是临时对象,不但浪费内存,还会影响GC效率。 - 为了能高效拼接字符串,Java标准库提供了StringBuilder,它是一个可变对象,可以预分配缓冲区,这样,往StringBuilder中新增字符时,不会创建新的临时对象: - 注意:对于普通的字符串+操作,并不需要我们将其改写为StringBuilder,因为Java编译器在编译时就自动把多个连续的+操作编码为StringConcatFactory的操作。在运行期,StringConcatFactory会自动把字符串连接操作优化为数组复制或者StringBuilder操作。 - StringBuffer,这是Java早期的一个StringBuilder的线程安全版本,它通过同步来保证多个线程操作StringBuffer也是安全的,但是同步会带来执行速度的下降。StringBuilder和StringBuffer接口完全相同,现在完全没有必要使用StringBuffer。 3. StringJoiner - 类似用分隔符拼接数组的需求很常见,所以Java标准库还提供了一个StringJoiner。 - String还提供了一个静态方法join(),这个方法在内部使用了StringJoiner来拼接字符串,在不需要指定“开头”和“结尾”的时候,用String.join()更方便。 4. 包装类型 - 5. #### 使用说明 1. xxxx 2. xxxx 3. xxxx #### 参与贡献 1. Fork 本仓库 2. 新建 Feat_xxx 分支 3. 提交代码 4. 新建 Pull Request #### 特技 1. 使用 Readme_XXX.md 来支持不同的语言,例如 Readme_en.md, Readme_zh.md 2. Gitee 官方博客 [blog.gitee.com](https://blog.gitee.com) 3. 你可以 [https://gitee.com/explore](https://gitee.com/explore) 这个地址来了解 Gitee 上的优秀开源项目 4. [GVP](https://gitee.com/gvp) 全称是 Gitee 最有价值开源项目,是综合评定出的优秀开源项目 5. Gitee 官方提供的使用手册 [https://gitee.com/help](https://gitee.com/help) 6. Gitee 封面人物是一档用来展示 Gitee 会员风采的栏目 [https://gitee.com/gitee-stars/](https://gitee.com/gitee-stars/)