Java
Java
Hello world!
psvm解释语句,main方法,main(参数),以上无参数,sout输出
注:
- 注意大小写
- 文件名和类名需要保持一致
- 命名规则,首字母小写 驼峰式命名
标识符
- 能字母,$,_,开头
- 不能以关键字作为变量名或方法名
- 注意大小写(大写的跟小写的不是同一个变量)
- 不能以特殊符号,数字来命名
- 下划线之后可以存在数字
占位符
%
表示占位符,因此,连续两个%%
表示一个%
字符本身
占位符 | 说明 |
---|---|
%d | 格式化输出整数 |
%x | 格式化输出十六进制整数 |
%f | 格式化输出浮点数 |
%e | 格式化输出科学计数法表示的浮点数 |
%s | 格式化字符串 |
数据类型
基本类型
注
拓展
整数
浮点数
float
接近但不等于,大约 (少用浮点数进行比较 有误差)
字符
强制转换成数字(字符的本质是数字)
编码
转义字符
\t 制表符
\n 换行
ps:
if (flag==true){}
和
if (flag){}
是一样的,只是写法不同
类型转换
强制转换 :(类型)变量名 高到底
自动转换: ... 低到高
- 不能对布尔值进行转换
- 存在内存溢出或精度问题
变量
变量作用域
赋值
变量的一个重要特征是可以重新赋值
第一次赋值时定义过了变量类型,第二次赋值时则不需要重新赋值
将一个变量的值重新赋值另一个变量
类变量
从属于类,随着类一起存在一起消失
实例变量
从属于对象,如果未设置初始化,输出类型的默认值
局部变量
必须声名和初始化值
定义在某个方法内,当程序执行完该方法的时候 无法调用该变量
常量
final 常量名 常量值
变量命名
运算符
自增 自减
逻辑运算符
b是假 上来就结束运行 称为短路运算
如果后面的运行 结果为6
位运算
当出现字符串时,后面的数字将会被转化成字符串类型显示出来 所以输出是1020
三元运算符
b ? x : y
包机制
包 其实就是文件夹
利用公司域名倒置作包名 eg:com.baidu.www
导入包
Doc
参数信息
@author 作者名
@version 版本号
@since 指明需要最早使用的的jdk版本
@return 返回值情况
@throws 异常抛出情况
生成Doc文档
命令行:javadoc 参数 java文件
javadoc -encoding UTF-8 -charset UTF-8 Doc.java
IDEA生成doc文档
生成doc
生成了
控制流程
输入
``System.in代表标准输入流
Scanner对象
通过Scanner类来获取用户的输入,通过Scanner类的next()
与nextLine()
方法获取输入的字符串,用hasNext()
与hasNextLine()
判断是否还有输入的数据
next
nextLine
简洁版
判断
求和 平均数
输出
``System.out代表标准输出流
顺序结构
选择结构
多选择结构
if,else,else if
switch case语句
case穿透
当没有break语句时,会将下面的语句全部输出,而不是可选模式
反编译
文件夹 拷贝文件
反编译文件
中文时通过哈希值来比较
判断引用类型相等
-
判断引用类型的变量是否相等用
==
-
判断引用类型的变量内容是否相等用
equals()
方法
循环结构
while循环
如果不满足条件,则不能进入循环
先判断后执行
do...while
即使不满足while循环条件,至少也会执行一次循环
先执行后判断
while和do..while循环的区别
for循环
for(初始化;布尔表达式;更新){
//代码
}
计算1-100之间的奇数和,偶数和
计算1-1000之间的能被5整除的数,并且3个数一换行
注:
println
输出完 会换行
print
输出完 不会换行
九九乘法表
增强for循环
语句更简便
break
和 continue
break
是强制退出循环,不执行剩下的语句
continue
用于终止某次循环过程,跳过循环体中尚未执行的语句,接着进行下一次是否执行循环的判定(再次回到寻魂开始的地方,从新开始循环)
标签
打印三角形
方法
Java方法是语句的集合,他们在一起执行一个功能
- 方法是解决一类问题的步骤的有序组合
- 方法包含于类或对象中
- 方法在程序中被创建,在其他地方被引用
- 方法的本意是功能块
- 一个方法只完成一个功能
void
表示 空
main方法要时刻保持简介干净,尽量将公共模块都提取到外面,通方法调用来实现功能
方法的定义
Java的方法类似于其他语言的函数,是一段用来完成特定功能的代码片段
定义方法的语句
方法包含一个方法头和一个方法体
形参和实参
方法调用
调用方法:对象名.方法名(实参列表)
-
当方法返回一个值得时候,方法调用通常被当作一个值
int larger = max(30,40);
-
当方法返回值是void,方法调用一定是一条语句
System.out.println("Hello,Yolo!")
方法重载
重载就是在一个类中,有相同的函数名称,但形参不同的函数
方法的重载的规则:
- 方法名称必须相同
- 参数列表必须不同(个数不同,或类型不同,参数排列顺序不同等)
- 方法的返回类型可以相同也可以不同
- 仅仅返回类型不同不足以成为方法的重载
可变参数(不定项参数)
递归
自己调用自己
递归的两部分
- 递归头:定义什么时候不调用自身方法。如果没有头,将陷入死循环
- 递归体:什么时候需要调用自身方法
基数大的不要用递归
计算器
数组
数组是相同类型数据的有序集合,是相同类型的若干个数据,按照一定的先后次序排雷组合而成
其中的每一个数据称作一个数组元素,每个数组元素可以通过一个下标来访问他们
格式
dataType[] arrayRefVar;
用 new 来创建数组
定义了什么类型的数组,就new什么类型的数组
dataType[] arrayRefVar = new dataType[arraySize];
eg:
nums = new int [10]; #可以存放10个int类型的数字
内存分析
数组边界
注意下标的合法区间 [0,length-1]
超出区间会报错
数组的使用
for循环
反转数组
多维数组
二维数组
int a[][] = new int[2][5];
冒泡排序
-
比较数组中,两个相邻的元素,如果第一个数比第二个数大,我们就交换他们的位置
-
每一次比较,都会产生出一个最大,或者最小的数字;
-
下一轮则可以少一次排序!
-
依次循环,直到结束!
自动排序
Arrays.sort(n);
稀疏数组
- 当一个数组中大部分元素为0,或者为同一值的数组时,可以用稀疏数组来保存数组
- 稀疏数组的处理方式
- 记录数组一共有几行几列,有多少个不同值
- 把具有不同值的元素而后行列及值记录爱意额小规模数组中,从而缩小程序的规模
转换为稀疏数组
获取有效值个数
稀疏数组还原
面向对象编程 和 面向过程编程 的区别
面向过程编程是 将任务一步步拆分,第一步干啥,第二步干啥,最后干啥
面向对象编程是 先有对象 然后与其进行互动
GirlFriend gf = new GirlFriend();
gf.name = "Alice";
gf.send("flowers");
创建对象,赋值,调用对象,并传递了个参数 Alice发送了一些 花
面向对象
本质
以类的方式组织代码,以对象的组织(封装)数据
基本概念
- 类
- 实例
- 方法
三大特性
(面向对象的实现方式)
-
封装
-
继承
-
多态
面向对象基础
Person zhang = new Person();
创建了Person类型的实例,通过变量zhang来指向它
Person zhang
是定义Person
是定义Person
类型的变量zhang
,new Person()
是创建Person
实例
方法
public
变成private
外部代码调用方法setName()
和setAge()
来间接修改private
字段
setAge()
就会检查传入的参数
注:类通过定义方法,可以给外部代码暴露一些操作的接口
定义方法
修饰符 方法返回类型 方法名(方法参数列表) {
若干方法语句;
return 方法返回值;
}
private方法
只有内部方法可以调用private
方法
以上代码没有定义age字段,获取age时,通过方法getAge()
返回的是一个实时计算的值,并非存储在某个字段的值。这说明方法可以封装一个类的对外接口,调用方不需要知道也不关心Person
实例在内部到底有没有age
字段
this变量
在方法内部调用,始终指向当前实例
没有命名冲突时可以省略
class Person {
private String name;
public String getName() {
return name; // 相当于this.name
}
}
class Person {
private String name;
public void setName(String name) {
this.name = name; // 前面的this不可少,少了就变成局部变量name了
}
}
方法参数
class Person {
...
public void setNameAndAge(String name, int age) {
...
}
}
当调用以上方法时,必须调用两个参数, string
和 int
Person zhang = new Person();
zhang.steNameAndAge("zhangsan",18)
可变参数
可变参数跟数组类型类似
参数绑定
调用方把参数传递给实例方法时,调用时传递的值会按参数位置一一绑定
基本类型的参数传递
以上,setAge
方法是将传入的值赋给Person
实例的 age
属性,变量 n
本身与 age
属性并没有绑定,所以 当 n
值改变时,p
的 age
属性保持不变
故 基本类型的参数传递 时局部变量和n
互不影响
传递引用参数
以上 参数是以一个数组,修改其内容,实例 p
的字段 name
的内容 也会被修改
引用类型参数的传递,调用方的变量,和接收方的参数变量,指向的是同一个对象。双方任意一方对这个对象的修改,都会影响对方(因为指向同一个对象)
调用方法
实例变量.方法名("参数")
zhang.setName("李华")
构造方法
创建实例时,通过构造方法实现将实例的值初始化完成
构造方法名是类名,对参数没有限制,在方法内部(跟普通方法象不没有返回值【包括void】)
注:
当我们定义了一个类而没有自定义构造方法时,编译器会自动生成一个默认的构造方法,默认构造方法里 没有参数和执行语句
class Person {
public Person() {
}
}
我们还可以定义两个构造方法 比如带参数的和不带参数的
public class Main {
public static void main(String[] args) {
Person p1 = new Person("Xiao Ming", 15); // 既可以调用带参数的构造方法
Person p2 = new Person(); // 也可以调用无参数构造方法
}
}
class Person {
private String name;
private int age;
public Person() {
}
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return this.name;
}
public int getAge() {
return this.age;
}
}
由于先执行初始化代码 在执行构造函数的代码 ,所以 new Person
的值由构造方法确定
多个构造方法
class Person {
private String name;
private int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public Person(String name) {
this.name = name;
this.age = 12;
}
public Person() {
}
}
如果调用new Person("Xiao Ming", 20);
,会自动匹配到构造方法public Person(String, int)
。
如果调用new Person("Xiao Ming");
,会自动匹配到构造方法public Person(String)
。
如果调用new Person();
,会自动匹配到构造方法public Person()
。
构造方法调用另一个构造方法
class Person {
private String name;
private int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public Person(String name) {
this(name, 18); // 调用另一个构造方法Person(String, int)
}
public Person() {
this("Unnamed"); // 调用另一个构造方法Person(String)
}
}
方法重载
方法名相同,但参数不同,叫方法重载(Overload)
功能类似的方法,使用同一方法名,调用起来方便
class Hello {
public void hello() {
System.out.println("Hello, world!");
}
public void hello(String name) {
System.out.println("Hello, " + name + "!");
}
public void hello(String name, int age) {
if (age < 18) {
System.out.println("Hi, " + name + "!");
} else {
System.out.println("Hello, " + name + "!");
}
}
}
可以有多个重载方法
继承
继承机制,可以复用代码
当我们在person
类的基础上新增student
类 student
类包含了person
类已有的字段和方法并且多了sorce
字段
class Person {
private String name;
private int age;
public String getName() {...}
public void setName(String name) {...}
public int getAge() {...}
public void setAge(int age) {...}
}
class Student {
private String name;
private int age;
private int score;
public String getName() {...}
public void setName(String name) {...}
public int getAge() {...}
public void setAge(int age) {...}
public int getScore() { … }
public void setScore(int score) { … }
}
根据继承机制 以上代码可以用 extends
关键字来实现继承
不需要重新定义age
和name
字段 只需要重新定义score
字段
class Person {
private String name;
private int age;
public String getName() {...}
public void setName(String name) {...}
public int getAge() {...}
public void setAge(int age) {...}
}
class Student extends Person {
private int score;
public int getScore() { … }
public void setScore(int score) { … }
注:子类自动获得了父类的所有字段,严禁定义与父类重名的字段!
在OOP的术语中,我们把Person
称为超类(super class),父类(parent class),基类(base class),把Student
称为子类(subclass),扩展类(extended class
感觉跟ssti的继承类似 每个类都会继承自某个类
也可以 某两个类继承于同一个类
protected
关键字
- 子类无法访问父类的
private
字段 或者private
方法 - 但是 只要将
private
修改为protected
就能被子类访问到 - 子类的子类 也可以访问其父类的
protected
protected
关键字所修饰的字段和防范 能够保证访问权限在继承树的内部
super
(父类)
super
表示父类,当子类引用父类的字段时,可以用 super.fileName
class Student extends Person {
public String hello() {
return "Hello, " + super.name;
}
}
除必要情况 也可以直接使用 this.anme
或者name
效果是一样的
当父类没有默认的构造方法时 子类就必须调用super();
public class Main {
public static void main(String[] args) {
Student s = new Student("Xiao Ming", 12, 89);
}
}
class Person {
protected String name;
protected int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
}
class Student extends Person {
protected int score;
public Student(String name, int age, int score) {
super(name, age); // 调用父类的构造方法Person(String, int)
this.score = score;
}
}
子类不会继承任何父类的构造方法。子类默认的构造方法是编译器自动生成的,不是继承的
使用sealed
修饰class,并通过permits
明确写出能够从该class继承的子类名称
public sealed class Shape permits Rect, Circle, Triangle {
...
}
向上转型
当 student
继承了 person
之后 student
就有了 perosn
的所有功能 可以实现一个引用类型为 person
的变量指向 student
类型的实例
Person p = new Student();
通过以上实例 可以发现 子类类型可以转换为更高层次的父类甚至是 object
向下转型
将父类类型强制转换成子类型
Person p1 = new Student();
Student s1 (Student) p1;
但是如果 p2一开始是指向Person的将无法转换
Person p2 = new Person();
Student s2 (Student) p2;
instanceof
操作符
Person p = new Person();
System.out.println(p instanceof Person); // true
System.out.println(p instanceof Student); // false
Student s = new Student();
System.out.println(s instanceof Person); // true
System.out.println(s instanceof Student); // true
Student n = null;
System.out.println(n instanceof Student); // false
判断instanceof
public static void main(String[] args) {
Object obj = "hello";
if (obj instanceof String s) {
// 可以直接使用变量s:
System.out.println(s.toUpperCase());
}
}
}
组合
student
和 person
是is
关系, student
和 book
是has
关系
is
关系是继承 has
关系是组合
class Student extends Person {
protected Book book;
protected int score;
}
多态
在继承关系中,子类如果定义了一个与父类方法签名完全相同的方法,被称为覆写(Override)
class Person {
public void run() {
System.out.println("Person.run");
}
}
在子类Student中,覆写这个run()方法:
class Student extends Person {
@Override //编译器会帮助检查是否覆写成功
public void run() {
System.out.println("Student.run");
}
}
计算税收的实例
// Polymorphic
public class Main {
public static void main(String[] args) {
// 给一个有普通收入、工资收入和享受国务院特殊津贴的小伙伴算税:
Income[] incomes = new Income[] {
new Income(3000),
new Salary(7500),
new StateCouncilSpecialAllowance(15000)
};
System.out.println(totalTax(incomes));
}
public static double totalTax(Income... incomes) {
double total = 0;
for (Income income: incomes) {
total = total + income.getTax();
}
return total;
}
}
class Income {
protected double income;
public Income(double income) {
this.income = income;
}
public double getTax() {
return income * 0.1; // 税率10%
}
}
class Salary extends Income {
public Salary(double income) {
super(income);
}
@Override
public double getTax() {
if (income <= 5000) {
return 0;
}
return (income - 5000) * 0.2;
}
}
class StateCouncilSpecialAllowance extends Income {
public StateCouncilSpecialAllowance(double income) {
super(income);
}
@Override
public double getTax() {
return 0;
}
}
在必要的情况下,我们可以覆写Object
的这几个方法。例如:
class Person {
...
// 显示更有意义的字符串:
@Override
public String toString() {
return "Person:name=" + name;
}
// 比较是否相等:
@Override
public boolean equals(Object o) {
// 当且仅当o为Person类型:
if (o instanceof Person) {
Person p = (Person) o;
// 并且name字段相同时,返回true:
return this.name.equals(p.name);
}
return false;
}
// 计算hash:
@Override
public int hashCode() {
return this.name.hashCode();
}
}
在子类的覆写方法中,如果要调用父类的被覆写的方法,可以通过super
来调用
继承可以允许子类覆写父类的方法。如果一个父类不允许子类对它的某个方法进行覆写,可以把该方法标记为final
。用final
修饰的方法不能被Override
class Person {
protected String name;
public final String hello() {
return "Hello, " + name;
}
}
class Student extends Person {
// compile error: 不允许覆写
@Override
public String hello() {
}
}
如果一个类不希望任何其他类继承自它,那么可以把这个类本身标记为final
。用final
修饰的类不能被继承:
final class Person {
protected String name;
}
// compile error: 不允许继承自Person
class Student extends Person {
}
字段同理
class Person {
public final String name = "Unamed";
}
给final字段重新赋值会报错
如果父类的方法本身不需要实现任何功能,仅仅是为了定义方法签名,目的是让子类去覆写它,那么,可以把父类的方法声明为抽象方法
抽象方法
如果一个class
定义了方法,但没有具体执行代码,这个方法就是抽象方法,抽象方法用abstract
修饰。
因为无法执行抽象方法,因此这个类也必须申明为抽象类(abstract class)。
接口
interface
用 它来声明一个接口
所谓interface
,就是比抽象类还要抽象的纯抽象接口,因为它连字段都不能有。因为接口定义的所有方法默认都是public abstract
的
当一个具体的class
去实现一个interface
时,需要使用implements
关键字。举个例子:
class Student implements Person {
private String name;
public Student(String name) {
this.name = name;
}
@Override
public void run() {
System.out.println(this.name + " run");
}
@Override
public String getName() {
return this.name;
}
}
接口继承
一个interface
可以继承自另一个interface
。interface
继承自interface
使用extends
,它相当于扩展了接口的方法。例如:
interface Hello {
void hello();
}
interface Person extends Hello {
void run();
String getName();
}
此时,Person
接口继承自Hello
接口,因此,Person
接口现在实际上有3个抽象方法签名,其中一个来自继承的Hello
接口
静态字段和静态方法
静态字段
public class Main {
public static void main(String[] args) {
Person ming = new Person("Xiao Ming", 12);
Person hong = new Person("Xiao Hong", 15);
ming.number = 88;
System.out.println(hong.number);
hong.number = 99;
System.out.println(ming.number);
}
}
class Person {
public String name;
public int age;
public static int number;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
}
推荐用类名来访问静态字段
Person.number = 99;
System.out.println(Person.number);
静态方法
静态方法需通过类名调用
静态方法属于class不属于实例
包
包没有父子关系 没有继承关系
解决包冲突的问题包名.类名
,如
小明的Person
类存放在包ming
下面,因此,完整类名是ming.Person
;
JDK的Arrays
类存放在包java.util
下面,因此,完整类名是java.util.Arrays
在定义class
的时候,我们需要在第一行声明这个class
属于哪个包
小明的Person.java
文件:
package ming; // 申明包名ming
public class Person {
}
包作用域
包作用域是指,一个类允许访问同一个 package
的没有 public
和 private
修饰的 class
,以及没有public
protected
private
修饰的字段和方法
位于同一个包的类,可以访问包作用域的字段和方法。不用public
、protected
、private
修饰的字段和方法就是包作用域。例如,Person
类定义在hello
包下面:
package hello;
public class Person {
// 包作用域:
void hello() {
System.out.println("Hello!");
}
}
当小明要引用小军的类时
package ming;
// 导入完整类名:
import mr.jun.Arrays;
public class Person {
public void run() {
// 写简单类名: Arrays
Arrays arrays = new Arrays();
}
}
定义为public
的类或者方法可以被其它类调用,前提是要有访问的权力
定义为private
的方法无法被其他类访问:
嵌套
定义在 class
内部的 class
就是嵌套类
用 final
可以阻止局部变量、方法被重新赋值,方法被子类覆写,class
被覆写
内部类
有一种类被定义在另一个类的内部,称为内部类(Inner Class)
class Outer {
class Inner {
// 定义了一个Inner Class
}
}
上述定义的Outer
是一个普通类,而Inner
是一个Inner Class,它与普通类有个最大的不同,就是Inner Class的实例不能单独存在,必须依附于一个Outer Class的实例。示例代码如下:
// inner class
public class Main {
public static void main(String[] args) {
Outer outer = new Outer("Nested"); // 实例化一个Outer
Outer.Inner inner = outer.new Inner(); // 实例化一个Inner
inner.hello();
}
}
class Outer {
private String name;
Outer(String name) {
this.name = name;
}
class Inner {
void hello() {
System.out.println("Hello, " + Outer.this.name);
}
}
}
Outer.Inner inner = outer.new Inner();
想要实例化Inner
,我们必须首先创建一个Outer
的实例,然后,调用Outer
实例的new
来创建Inner
实例
观察Java编译器编译后的.class
文件可以发现,Outer
类被编译为Outer.class
,而Inner
类被编译为Outer$Inner.class
。
Static Nested Class
最后一种内部类和Inner Class类似,但是使用static
修饰,称为静态内部类(Static Nested Class):
// Static Nested Class
public class Main {
public static void main(String[] args) {
Outer.StaticNested sn = new Outer.StaticNested();
sn.hello();
}
}
class Outer {
private static String NAME = "OUTER";
private String name;
Outer(String name) {
this.name = name;
}
static class StaticNested {
void hello() {
System.out.println("Hello, " + Outer.NAME);
}
}
}
用static
修饰的内部类和Inner Class有很大的不同,它不再依附于Outer
的实例,而是一个完全独立的类,因此无法引用Outer.this
,但它可以访问Outer
的private
静态字段和静态方法。如果把StaticNested
移到Outer
之外,就失去了访问private
的权限。
classpath
classpath
是JVM用到的一个环境变量,它用来指示JVM如何搜索class
Jar包
将目录打包
字符串和编码
String
是一个引用类型
String s1 = "Hello";
实际上字符串在String
内部是通过一个char[]
数组表示的,因此,按下面的写法也是可以的:
String s2 = new String(new char[] {'H', 'e', 'l', 'l', 'o', '!'});
字符串比较
比较两个字符串的内容是否相等 要用equals()方法
public class Main {
public static void main(String[] args) {
String s1 = "hello";
String s2 = "hello";
System.out.println(s1 == s2);
System.out.println(s1.equals(s2));
}
}
索引从零开始
搜索子串的更多的例子:
"Hello".indexOf("l"); // 2
"Hello".lastIndexOf("l"); // 3
"Hello".startsWith("He"); // true
"Hello".endsWith("lo"); // true
提取子串的例子:
"Hello".substring(2); // "llo"
"Hello".substring(2, 4); "ll"
去除首尾空白字符
使用trim()
方法可以移除字符串首尾空白字符。空白字符包括空格,\t
,\r
,\n
" \tHello\r\n ".trim();
使用 isEmpty()
和 isBlank()
来判断字符串是否为空和空白字符串
替换字串
要在字符串中替换子串,有两种方法。一种是根据字符或字符串替换:
String s = "hello";
s.replace('l', 'w'); // "hewwo",所有字符'l'被替换为'w'
s.replace("ll", "~~"); // "he~~o",所有子串"ll"被替换为"~~"
另一种是通过正则表达式替换:
String s = "A,,B;C ,D";
s.replaceAll("[\\,\\;\\s]+", ","); // "A,B,C,D"
格式化字符串
字符串提供了formatted()
方法和format()
静态方法,可以传入其他参数,替换占位符,然后生成新的字符串:
// String
public class Main {
public static void main(String[] args) {
String s = "Hi %s, your score is %d!";
System.out.println(s.formatted("Alice", 80));
System.out.println(String.format("Hi %s, your score is %.2f!", "Bob", 59.5));
}
}
有几个占位符,后面就传入几个参数。参数类型要和占位符一致。我们经常用这个方法来格式化信息。常用的占位符有:
%s
:显示字符串;%d
:显示整数;%x
:显示十六进制整数;%f
:显示浮点数。
类型转换
使用 valueof
String.valueOf(123); // "123"
String.valueOf(45.67); // "45.67"
String.valueOf(true); // "true"
String.valueOf(new Object()); // 类似java.lang.Object@636be97c