2021.8.23
【private field 私人字段(只允许内部调用)/ public field公共字段(外部可调用)】
class Person { //为了避免外部直接访问field
private String name; //使用了privatea,拒绝外部访问
private int age; //但是private允许类内部调用
} //所以要想改变里面的值,一般在类里面再构造一个函数调用进行更改
【调用方法】
public class Main {
public static void main(String[] args) {
Person ming = new Person();
ming.setName("Xiao Ming"); // 设置name
System.out.println(ming.getName());
}
}
class Person {
private String name;
private int age;
public String getName() {
return this.name;
}
public void setName(String name) {
this.name = name;
}
}
【private方法】 :定义private方法的理由是内部方法是可以调用private方法的
【this变量】:类似python的self,指向当前实例,没有命名冲突时可以省略。
【方法参数】:
public void setNameAndAge(String name, int age){}
【可变参数】:类型...
public void setNames(String... names) {}
【参数绑定】
String为不可变类,会用一个新的地址引用
【初始化实例】:通过在内部构造一个名字相同的方法,就可以在实例化时一起初始化
public class Main {
public static void main(String[] args) {
Person p = new Person("Xiao Ming", 15);
}
class Person {
private String name;
private int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
}
【默认构造方法】 :没有定义时,系统会默认构造
class Person {
public Person() {
}
}
字段的初始化时null,基本数组类型时默认值
既可以在类中,设置参数初始值,也可以在实例化时传入参数,运行顺序
先初始化字段,int age = 10;
再执行构造方法的代码进行初始化,new Person("Xiao Ming", 12)。
【多构造方法】:存在多个构造方法,根据传入的参数和类型来判断使用哪个构造方法,也有通过调用其他构造方法,语法是this(...)
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。比如string类中的index0f,就可以查字符的,字符串的。
int indexOf(int ch)
:根据字符的Unicode码查找;
int indexOf(String str)
:根据字符串查找;
【extends关键词】:子类自动获得父类的所有字段,严禁定义与父类重名的字段!
class Person {
private String name;
private int age;
}
class Student extends Person {
// 不要重复name和age字段/方法,
// 只需要定义新增score字段/方法:
private int score;
public int getScore() { … }
public void setScore(int score) { … }
}
【object类】:再没有extends 对象时,会默认继承object类
【protect关键词】:一般的继承,子类无法访问父类中的private,如果需要访问的话,要把private改为protected修饰。
【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(),但是会报错,
//因为Person的构造方法需要传入name ,age两个参数. 没有默认构造方法
//正确要的时:super(name,age)
this.score = score;
}
}
【阻止继承】 :感觉现阶段应该很少用。
【向上转型】
Student s = new Student();
Person p = s; // upcasting, ok //一个person类的变量 指向studnet
Object o1 = p; // upcasting, ok //一个object类的变量,指向person
Object o2 = s; // upcasting, ok 继承树 student ---》 person ---->object
【向下转型】
Person p1 = new Student(); // upcasting, ok
Person p2 = new Person();
Student s1 = (Student) p1; // ok
Student s2 = (Student) p2; // runtime error! ClassCastException!
//实际上p2指向的时person,而p1指向的student,因为子类会有比父类多更多的功能,转型就会出问题
instanceof
后,可以直接转型为指定变量,无需再强制转型。 Object obj = "hello";
if (obj instanceof String) {
String s = (String) obj;
System.out.println(s.toUpperCase());
}
-------------------------------------------------
public class Main {
public static void main(String[] args) {
Object obj = "hello";
if (obj instanceof String s) {
// 可以直接使用变量s:
System.out.println(s.toUpperCase());
}
}
}
【组合和继承】
【覆写override】:子类和父类的方法名相同,方法参数相同,返回值相同,就是覆写。
【多态】:可以允许添加更多的子类实现更多的功能,而没改动父类的代码,要反复理解廖老师的例子中的income有多个态。
【覆写基类object方法】:一般覆写object的tostring,equals,hashcode。
【调用super】:如果要调用父类的被覆写方法,要super来调用
class Person {
protected String name;
public String hello() {
return "Hello, " + name;
}
}
Student extends Person {
@Override
public String hello() {
// 调用父类的hello()方法:
return super.hello() + "!";
}
}
【final关键词】
可以不允许子类对自己某个方法进行覆写
可以不允许其他类继承自己
可以不允许对该值进行修改
class Person { //子类无法覆写hello
protected String name;
public final String hello() {
return "Hello, " + name;
}
}
final class Person { //此Person无法被继承
protected String name;
}
class Person { // name不允许被修改
public final String name = "Unamed";
}
多态的存在,每个子类都会对父类的方法进行覆写,使得父类的该方法显得比较没有意义,但是又不能删除,所以出现了抽象这个概念
【abstract】:如果一个class定义了方法,但没有具体执行代码,这个方法就是抽象方法,用abstract修饰。
abstract class Person { //抽象类是 不能被实例化的,只能被继承
public abstract void run();
}
面向抽象编程的本质就是:
上层代码只定义规范(例如:abstract class Person
);
不需要子类就可以实现业务逻辑(正常编译);
具体的业务逻辑由不同的子类实现,调用者并不关心。
如果一个抽象类没有字段(一些类型的申明),所有方法全部都是抽象方法,那么可以把抽象类改写为接口interface,接口不可以实例化
abstract class Person {
public abstract void run();
public abstract String getName();
}
//改写
interface Person {
void run();
String getName();
}
【继承关系】:建议反复观看
【dafualt方法】:在接口中,可以定义dafualt方法
public class Main {
public static void main(String[] args) {
Person p = new Student("Xiao Ming");
p.run();
}
}
interface Person {
String getName();
default void run() {
System.out.println(getName() + " run");
}
}
class Student implements Person {
private String name;
public Student(String name) {
this.name = name;
}
public String getName() {
return this.name;
}
}
dafualt方法的目的就是,当我们需要给接口添加一个新方法时,会对涉及的子类都进行更改,如果新增的是dafualt,那么子类就不需要全部修改,只在需要的子类进行覆写就好。
dafault方法和抽象类的普通方法有所不同,因为interface没有字段,default无法访问字段。
【static field】:静态字段有一个共享的空间
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;
}
}
// 88 99
实例可以访问静态字段,实际上是它们指向的是person class的静态字段。
因此,不推荐用实例变量.静态字段
去访问静态字段,因为在Java程序中,实例对象并没有静态字段。在代码中,实例对象能访问静态字段只是因为编译器可以根据实例类型自动转换为类名.静态字段
来访问静态对象。
推荐用类名来访问静态字段。可以把静态字段理解为描述class
本身的字段(非实例字段)。对于上面的代码,更好的写法是:
Person.number = 99;
【静态方法】:不需要通过实例变量来调用,可以直接通过类名调用
public class Main {
public static void main(String[] args) {
Person.setNumber(99);
System.out.println(Person.number);
}
}
class Person {
public static int number;
public static void setNumber(int value) {
number = value;
}
}
和静态字段一样,属于类不是与实例,因此静态方法内部不可以调用this变量,也无法访问实例字段,只能访问静态字段。
通过实例变量也可以调用静态方法,但这只是编译器自动帮我们把实例改写成类名而已。
【接口静态字段】:接口里没有实例字段,但是可以有静态字段,并且必须为final类型
public interface Person {
public static final int MALE = 1;
//实际上可以直接int MALE,因为编译器会自动帮我变为public static final
public static final int FEMALE = 2;
}
【package】:在定义class时,在第一行声明这个class属于那个包,有点创建自己的包的意思
package ming; // 申明包名ming
public class Person {
}
【import】 :导包
【import static】:导入包中的静态字段,很少用
Java编译器最终编译出的.class
文件只使用完整类名,因此,在代码中,当编译器遇到一个class
名称时:
如果是完整类名,就直接根据完整类名查找这个class
;
如果是简单类名,按下面的顺序依次查找:
查找当前package
是否存在这个class
;
查找import
的包是否包含这个class
;
查找java.lang
包是否包含这个class
。
因此编写class时,编译器会自动帮我们import两个动作
// Main.java
package test;
import java.text.Format;
public class Main {
public static void main(String[] args) {
java.util.List list; // ok,使用完整类名 -> java.util.List
Format format = null; // ok,使用import的类 -> java.text.Format
String s = "hi"; // ok,使用java.lang包的String -> java.lang.String
System.out.println(s); // ok,使用java.lang包的System -> java.lang.System
MessageFormat mf = null; // 编译错误:无法找到MessageFormat: MessageFormat cannot be resolved to a type
}
}
2021.8.24
public class Main {
public static void main(String[] args) {
Outer outer = new Outer("Nested"); // 实例化一个Outer
Outer.Inner inner = outer.new Inner(); // 实例化一个Inner
inner.hello();
}
}
【public】:可以被其他类访问,在不同的package之间访问需要public,同一个package就无所谓
【private】:只能在class内部访问
【protected】:允许子类访问
【package】:允许同一个包内,访问一个没有public,private修饰的类,以及没有public,private,protected修饰的字段和方法
.java
文件只能包含一个public
类,但可以包含多个非public
类。如果有public
类,文件名必须和public
类的名字相同。class Outer {
class Inner {
// 定义了一个Inner Class
}
}
【内部类】:不能直接实例化,要先实例一个Outer,在通过Outer new一个新的Inner,内部类可以调用Private,也可以用this
public class Main {
public static void main(String[] args) {
Outer outer = new Outer("Nested"); // 实例化一个Outer
Outer.Inner inner = outer.new Inner(); // 实例化一个Inner
inner.hello();
}
}
【匿名类】:待补充,暂时看不懂【unknown】
【静态内部类】:Static Nested Class是独立类,但只拥有Outer Class的private
访问权限。
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);
}
}
}
【classpath】:环境变量的配置路径
【jar】:jar压缩包,包含很多class文件,方便下载和使用
拓展知识,先跳过【unknow】
【string操作】
string s1 = "Wudibooo";
string s2 = "WUDIBOOO"
//比较 ,不要用==,因为string是引用类型不是基本数值类型
s1.equals(s2);
s1.equalsIgnoreCase(s2);
//包含
"Hello".contains("ll"); // true
//搜索子串
"Hello".indexOf("l"); // 2
"Hello".lastIndexOf("l"); // 3
"Hello".startsWith("He"); // true
"Hello".endsWith("lo"); // true
//substring
//提取子串
"Hello".substring(2); // "llo"
"Hello".substring(2, 4); "ll"
//trim strip(这个还可以移除中文空白字符)
//移除首尾的空白字符 \t \r \n " "
" \tHello\r\n ".trim(); // "Hello"
"\u3000Hello\u3000".strip(); // "Hello"
//replace
String s = "hello";
s.replace('l', 'w'); // "hewwo",所有字符'l'被替换为'w'
//分割split
String s = "A,B,C,D";
String[] ss = s.split("\\,"); // {"A", "B", "C", "D"}
//拼接join
String[] arr = {"A", "B", "C"};
String s = String.join("***", arr); // "A***B***C"
【格式化字符串】:
常用占位符
%s
:显示字符串;%d
:显示整数;%x
:显示十六进制整数;%f
:显示浮点数。 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));
}
}
【类型转换】
任何类型转string,用value0f()
String.valueOf(123); // "123"
String.valueOf(45.67); // "45.67"
String.valueOf(true); // "true"
String.valueOf(new Object()); // 类似java.lang.Object@636be97c
string转其他型
//int
int n1 = Integer.parseInt("123"); // 123
int n2 = Integer.parseInt("ff", 16); // 按十六进制转换,255
//boolean
boolean b1 = Boolean.parseBoolean("true"); // true
boolean b2 = Boolean.parseBoolean("FALSE"); // false
string和char互相转换
public class Main {
public static void main(String[] args) {
char[] cs = "Hello".toCharArray();
String s = new String(cs);
System.out.println(s); //Hello
cs[0] = 'X';
System.out.println(s); //Hello
}
}
需要注意的是,如果修改char[]的值,string并不会改变
如果要想通过修改char [] 来对string进行修改,可以使用下面的写法
public class Main {
public static void main(String[] args) {
int[] scores = new int[] { 88, 77, 51, 66 };
Score s = new Score(scores);
s.printScores(); //[88, 77, 51, 66]
scores[2] = 99;
s.printScores(); //[88, 77, 99, 66]
}
}
class Score {
private int[] scores;
public Score(int[] scores) {
this.scores = scores;
}
public void printScores() {
System.out.println(Arrays.toString(scores));
}
}
【转换编码】 :转换编码就是将String
和byte[]
转换,需要指定编码,转换为byte[]
时,始终优先考虑UTF-8
编码
2021.8.25
对string做特殊处理时,可以是用 ‘ + ’号,但是每一次都会扔掉旧的字符串,造成内存浪费,所以java提供了stringBuilder,是一个可变的对象。
StringBuilder sb = new StringBuilder(1024);
for (int i = 0; i < 1000; i++) {
sb.append(',');
sb.append(i);
}
String s = sb.toString();
如果简单的 ‘ + ’,不需要stringBuildr,因为编译器会自动把 + 变回stringConcatFactory 操作
【stringJoiner()】:拼接对象
public class Main {
public static void main(String[] args) {
String[] names = {"Bob", "Alice", "Grace"};
var sj = new StringJoiner(", ", "Hello ", "!"); // 分隔号, 开头,结尾
for (String name : names) {
sj.add(name);
}
System.out.println(sj.toString());
}
}
【string.join()】:更方便,但是没有开头和结尾
Java的数据分为,基本类型和引用类型。
想将基本类型变为一个应用类型,我们需要进行包装,将基本类型变为类里的一个静态字段。
public class Integer {
private int value;
public Integer(int value) {
this.value = value;
}
public int intValue() {
return this.value;
}
}
java自己提供了很多包装类。
基本类型 | 对应的引用类型 |
---|---|
boolean | java.lang.Boolean |
byte | java.lang.Byte |
short | java.lang.Short |
int | java.lang.Integer |
long | java.lang.Long |
float | java.lang.Float |
double | java.lang.Double |
char | java.lang.Character |
如何使用:
public class hellow {
public static void main(String[] args) {
int i = 100;
Integer n1 = new Integer(i); //不推荐使用NEW的方法
Integer n2 = Integer.valueOf(i); //使用valueOf 里面可以放入int或string
Integer n3 = Integer.valueOf("100");
}
}
【自动装箱】:
int i = 100;
Integer n = Integer.valueOf(i);
int x = n.intValue();
正常Interger 和int的转换是这样,实际我们可以省略,
Integer n = 100; // 编译器自动使用Integer.valueOf(int) ,自动装箱
int x = n; // 编译器自动使用Integer.intValue() ,自动拆箱
【不变类】:源码使用的是private,不可以外部更改,而且不建议是用 == 做比较。
【进制转换】:Interger内部提供了静态方法parseInt(),可以把String转INT
int n1 = Interger.parseInt("100"); //默认 10进制
int n1 = Interger.parseInt("100",16) //16进制
也可以转制定进制的字符串
String s = Integer.toString(100)
String s1 = Integer.toHexString(100)
【Number
】 所有的整数和浮点数的包装类型都继承自Number
// 向上转型为Number:
Number num = new Integer(999);
// 获取byte, int, long, float, double:
byte b = num.byteValue();
int n = num.intValue();
long ln = num.longValue();
float f = num.floatValue();
double d = num.doubleValue();
是一种命名规范,通过getter和setter来定义属性,属性也只是一种叫法
public String getName() { return this.name; } //getter
public void setName(String name) { this.name = name; } //setter
【enum关键词】:enumerate,枚举
可以让编译器自动检查某个值在不在枚举的集合里,要注意的是enum对类型的非常严格,不同用途的枚举需要不同的类型来标记。
public class Main {
public static void main(String[] args) {
Weekday day = Weekday.SUN;
if (day == Weekday.SAT || day == Weekday.SUN) {
System.out.println("Work at home!");
} else {
System.out.println("Work at office!");
}
}
}
enum Weekday { //enum常量本身带有类型信息,即Weekday.SUN类型是Weekday,类型不同会报错
SUN, MON, TUE, WED, THU, FRI, SAT;
}
【emun比较】:这是因为enum
类型的每个常量在JVM中只有一个唯一实例,所以可以直接用==
【emun特点】:
enum
类型总是继承自java.lang.Enum
,且无法被继承;enum
的实例,而无法通过new
操作符创建enum
的实例;enum
类型用于switch
语句。String s = Weekday.SUN.name(); // "SUN" 返回常量名
int n = Weekday.MON.ordinal(); // 1 返回定义的常量的顺序,从0开始计数
在ordinal()时,枚举集合里的顺序不小心修改了,就会导致ordinal的返回值也变化,这是不利的。通过给枚举常量添加字段,可以解决这问题
public class Main {
public static void main(String[] args) {
Weekday day = Weekday.SUN;
if (day.dayValue == 6 || day.dayValue == 0) {
System.out.println("Work at home!");
} else {
System.out.println("Work at office!");
}
}
}
enum Weekday {
MON(1), TUE(2), WED(3), THU(4), FRI(5), SAT(6), SUN(0);
public final int dayValue;
private Weekday(int dayValue) {
this.dayValue = dayValue;
}
}
还可以填加string,增加可读性
public class Main {
public static void main(String[] args) {
Weekday day = Weekday.SUN;
if (day.dayValue == 6 || day.dayValue == 0) {
System.out.println("Today is " + day + ". Work at home!");
} else {
System.out.println("Today is " + day + ". Work at office!");
}
}
}
enum Weekday {
MON(1, "星期一"), TUE(2, "星期二"), WED(3, "星期三"), THU(4, "星期四"), FRI(5, "星期五"), SAT(6, "星期六"), SUN(0, "星期日");
public final int dayValue;
private final String chinese;
private Weekday(int dayValue, String chinese) {
this.dayValue = dayValue;
this.chinese = chinese;
}
@Override
public String toString() {
return this.chinese;
}
}
用在switch中
public class hellow {
public static void main(String[] args) {
Weekday person = Weekday.boy;
switch (person) {
case boy:
System.out.println("" + person);break;
case girl:
System.out.println("" + person); break;
case man:
System.out.println("" + person);break;
case wuman:
System.out.println("" + person);break;
}
}
}
enum Weekday{
boy,girl,man,wuman;
}
【record类】:java14开始,用record关键词,可以一行写出一个不变类
public record Point(int x, int y) {}
等于,不仅定义了字段,还覆写了toString,equals,hashcode
public final class Point extends Record {
private final int x;
private final int y;
public Point(int x, int y) {
this.x = x;
this.y = y;
}
public int x() {
return this.x;
}
public int y() {
return this.y;
}
public String toString() {
return String.format("Point[x=%s, y=%s]", x, y);
}
public boolean equals(Object o) {
...
}
public int hashCode() {
...
}
}
【record 里构造方法】: 自动创建的话,我们要想检查参数,要这样
public record Point(int x, int y) {
public Point(int x, int y) { //称为Compact Constructor
// 这是我们编写的Compact Constructor:
if (x < 0 || y < 0) {
throw new IllegalArgumentException();
}
}
//也可以加上自己的静态方法
public static Point fun() {
return new Point(0, 0);
}
}
var z = Point.fun();
编译器会编译成
public final class Point extends Record {
public Point(int x, int y) {
// 这是我们编写的Compact Constructor:
if (x < 0 || y < 0) {
throw new IllegalArgumentException();
}
// 这是编译器继续生成的赋值代码:
this.x = x;
this.y = y;
}
...
}
【tips】
数据大小不够用的时候,就建一个bigInteger
BigInteger
用于表示任意大小的整数;BigInteger
是不变类,并且继承自Number
;BigInteger bi = new BigInteger("1234567890");
System.out.println(bi.pow(5));
// 2867971860299718107233761438093672048294900000
【类型转换】
BigInteger i = new BigInteger("123456789000");
System.out.println(i.longValue()); // 123456789000
byte
:byteValue()
short
:shortValue()
int
:intValue()
long
:longValue()
float
:floatValue()
double
:doubleValue()
如需要精准的,可以用intValueExact(),longValueExact()
类似BigInterger,同样继承自Number
【BigDecimal】
BigDecimal d1 = new BigDecimal("123.4567");
//用scale表示小数位
System.out.println(d1.scale()); // 4,4位小数
//如果返回的是负值,表示此数是整数,-2,就表示是整数,且末尾有2个零
【BigDecimal.stripTrailingZeros()】:跳过末尾的zero
【四则运算】:+ - *没问题,除法在除不尽的时候,要设置保留的位数
BigDecimal d1 = new BigDecimal("123.456");
BigDecimal d2 = new BigDecimal("23.456789");
BigDecimal d3 = d1.divide(d2, 10, RoundingMode.HALF_UP); // 保留10位小数并四舍五入
BigDecimal d4 = d1.divide(d2); // 报错:ArithmeticException,因为除不尽
【divideAndRemainder()】:返回商和余数,通常用于判断是否是某个数的整倍
BigDecimal n = new BigDecimal("12.75");
BigDecimal m = new BigDecimal("0.15");
BigDecimal[] dr = n.divideAndRemainder(m);
if (dr[1].signum() == 0) {
// n是m的整数倍
}
【比较】
【Math】
【Random】
在不给定种子的时候,是根据计算机的时间搓来计算的
Random r = new Random();
r.nextInt(); // 2071575453,每次都不一样
r.nextInt(10); // 5,生成一个[0,10)之间的int
r.nextLong(); // 8811649292570369305,每次都不一样
r.nextFloat(); // 0.54335...生成一个[0,1)之间的float
r.nextDouble(); // 0.3716...生成一个[0,1)之间的double
【SecureRandom】
生成安全的随机数,要用再看吧。