一、Java特性
1. 特性
(1) 封装
封装指的是私有化,即隐藏具体属性和实现细节,仅对外开放接口,控制程序中属性的访问级别。
(2) 继承
子类拥有父类的所有属性和方法(除了 private
修饰的属性不能拥有),从而实现了代码的复用。
(3) 多态
对象在不同时刻表现出来的不同状态,最常见的即方法的重载和重写。
2. 接口
- 接口没有构造方法,不能实例化对象,只能通过接口实现使用。
- 一个类只能继承一个父类,而一个类却可以实现多个接口。
- 实现类通过
implements
关键字实现接口类,且必须重写除默认方法外的所有接口方法。 - 当接口方法声明为
default
时可包含方法体,实现类可选择是否重写,若未重写方法内容在调用时则执行默认方法体内容。 - 通过
static
可定义静态接口方法,接口方法可包含方法体,实现类无法重写静态接口方法。
public interface TestService {
/**
* 普通接口方法,无方法体,实现类必须重写
*/
int AddElem(int a, int b);
/**
* 默认接口方法,可包含方法体
*/
default void sayHello() {
System.out.println("Hello World !");
}
/**
* 静态接口方法,无法被实现类重写
*/
static void sayGoodbye() {
System.out.println("Goodbye.");
}
}
/**
* 实现类可以不实现默认方法,但必须重写普通方法
*/
public class TestServiceImpl implements TestService {
@Override
public int AddElem(int a, int b) {
return a + b;
}
}
public class InterfaceTest {
public static void main(String[] args) {
TestService service = new TestServiceImpl();
// 执行默认接口方法,打印 Hello World
service.sayHello();
// 通过接口类调用静态接口方法
TestService.sayGoodbye();
}
}
3. 抽象
抽象与接口类似,都是基于对应的共性抽象与多实现,其显著的一个区别在与抽象允许构造函数创建对象,因此可实现一些数据的初始化。
- 抽象类不能实例化对象
(new)
,只能通过被继承使用。 - 抽象方法通过
abstract
声明,没有方法体(同接口方方法)。 - 抽象类可以不包含抽象方法,但包含抽象方法的类必须声明为抽象类。
- 继承抽象类的子类必须重写其的抽象方法,或者声明自身为抽象类。
- 抽象类可以包含普通方法,其子类可选择是否重写,作用与普通类继承一致。
public abstract class AbsFather {
/**
* 抽象方法,子类必须进行重写或声明为抽象类
*/
public abstract int AddElem(int a, int b);
/**
* 普通方法,子类可选择性重写
*/
public void hello() {
System.out.println("Father say hello.");
}
}
/**
* 子类必须继承父类所以抽象方法,否则必须声明为抽象类
*/
public class AbsSon extends AbsFather {
/**
* 重写父类抽象方法
*/
@Override
public int AddElem(int a, int b) {
return a + b;
}
}
public class AbsTest {
public static void main(String[] args) {
// 通过子类实例化
AbsFather absSon = new AbsSon();
int sum = absSon.AddElem(10, 20);
System.out.println(sum);
// 执行父类 hello() 方法
absSon.hello();
}
}
二、基本知识
1. 重载重写
(1) 方法重写
方法重写即当类实现接口或继承父类时,可对父类中的方法进行覆盖重写。
在 Java
中,如果两个对象在 equals()
方法中相等,那么它们的 hashCode()
值也必须相等。换句话说,两个对象的 hashCode()
值不同,则它们不可能相等,这是因为在常见的如 Map、HashMap
等集合中 hashCode()
值是用于确定对象在哈希表中的位置,以便进行高效的查找和访问。如果两个对象在 equals()
方法中相等,但它们的 hashCode()
值不同,那么它们可能会被放入哈希表中的不同位置,而不是预期的同一位置,这将导致在使用哈希表进行查找和访问时出现问题。因此若需要重写对象的 equlas()
方法时,需要同时重写其的 hashCode()
方法。
在实现 hashCode()
方法时,通常使用对象的属性值来计算一个整数,这个整数应该尽量不与其他对象的 hashCode()
值重复,从而保证不同对象由 hashCode()
计算得到的哈希值一定是不同的。
(2) 方法重载
方法重载的体现方式为方法名相同,但参数的数量或者类型不同,最常见的即类的构造方法。
如下示例中的 User()
与 User(String id, String name)
即方法重载。
public class User {
private String id;
private String name;
public User() {
}
public User(String id, String name) {
this.id = id;
this.name = name;
}
}
2. 内部类
(1) 匿名类
匿名类 (Anonymous Class)
是一种没有名字的局部类,可以用来实现某个接口或继承某个类,通常用来编写简单的逻辑处理或回调函数。
匿名类的声明方式如下:
new SomeClass() {
// 实现接口或继承类的方法
};
(2) 内部类
内部类 (Inner Class)
是一种定义在另一个类内部的类,可以被声明为私有、公共或受保护的。与匿名类不同的是,内部类可以有自己的名字和构造函数,而且可以继承其他类或实现接口。
内部类的作用范围可以是定义它的类的方法或代码块内部,也可以是其他类的方法或代码块内部(需要通过外部类的实例来创建内部类的实例)。
内部类的声明方式如下:
class OuterClass {
// ...
class InnerClass {
//...
}
}
三、关键字
1. this
当实例变量和方法形参重名冲突时,``this` 关键字指代实例变量。
public class Test {
int num = 1024;
public static void main(String[] args) {
printNum(2048);
}
public void printNum(int num) {
// 输出1024
System.out.println("Parameter num: " + num);
// 输出2048
System.out.println("Instance variable num: " + this.num);
}
}
2. static
(1) 类变量
static
修饰的变量为类变量,相同类的实例对象的类变量存储于同一内存,任何实例对象对其进行操作都会影响到其它实例对象对其访问。
类变量通常通过 类名.变量名
进行访问,而非实例对象。
public class Test {
static int staticNum = 4096;
public static void main(String[] args) {
printNum(2048);
}
public static void printNum(int num) {
System.out.println("Parameter num: " + num);
System.out.println("Parameter num: " + Test.staticNum);
}
}
(2) 静态代码块
static
除了用于修饰变量和方式之外,还可以以静态代码块的形式独立存在,当 main
执行时会根据静态块出现顺序执行。
需要注意一点,静态块中可以通过 类名.变量名
的形式访问静态变量,但无法直接访问实例变量,只能通过新建对象进行访问。
public class StaticTest {
private int num1; // 实例变量
private static int num2; // 静态变量
public static void main(String[] args) {
// 按静态块出现的先后顺序执行其方法体
}
static {
// 直接赋值 num1 = 10; 是非法的
StaticTest test = new StaticTest();
test.num1 = 10;
System.out.println("静态块 1 : " + test.num1);
}
static {
StaticTest.num2 = 20;
System.out.println("静态块 2 : " + num2);
}
}
3. instanceof
通过 instanceof
可以判断变量的类型,需要注意类的子类得到结果仍为 true
。
class Human {
// ...
}
class Student extends Human {
// ...
}
public void InstanceDemo() {
Human human = new Human();
Student student = new Student();
System.out.println(student instanceof human);
}
四、数据类型
1. Number
Number
是所有数值类型的父类,当不确定后续将会使用何种类型即可使用 Number
类型。
方法 | 作用 |
---|---|
byteValue() | 获取数值的 byte 类型。 |
intValue() | 获取数值的 int 类型。 |
floatValue() | 获取数值的 float 类型。 |
doubleValue() | 获取数值的 double 类型。 |
longValue() | 获取数值的 long 类型。 |
public void NumberDemo() {
Number number = 10;
byte b = number.byteValue();
System.out.println("byte: " + b);
int i = number.intValue();
System.out.println("int: " + i);
float f = number.floatValue();
System.out.println("float: " + f);
double d = number.doubleValue();
System.out.println("double: " + d);
long l = number.longValue();
System.out.println("long: " + l);
}
2. BigDecimal
普通的 int
型数字长度不能超过 11
位,而 BigDecimal
可以处理任意长度的数字序列长度。其相对应的 + - * /
基本运算则由 add(), subtract(), multiply(), divide()
等函数代替。
public void Demo(){
BigInteger ten = BigInteger.valueOf(10);
BigInteger twenty = BigInteger.valueOf(20);
System.out.println(ten.add(twenty)); // 10 + 20 = 30
System.out.println(twenty.subtract(ten)); // 20 - 10 = 10
System.out.println(ten.multiply(twenty)); // 10 * 20 = 200
System.out.println(twenty.divide(ten)); // 20 / 10 = 2
}
3. String
String
与其它基本类型不同的是其被声明为 final
,即一旦被定义声明则后续无法进行改变。
默认创建的字符串将会存在常量池中,后续若定义内容相同的字符串将从常量池直接引用已存在对象,不会重新创建对象,只有通过 new
关键字才是真正意义的对象创建。
public void demo() {
/*
* 保存在字符串常量池中, 如果在后续代码中再次定义字符串“abc”,
* 那么实际上使用的是之前创建的字符串对象,而不是新创建一个对象。
*/
String a = "test";
/*
* 保存在堆中创建的,而不是保存在字符串常量池中。
* 每次使用构造函数创建字符串对象时,都会在堆中创建一个新的对象。
*/
String b = new String("test");
System.out.println(a == b);
}
(1) 常用方法
字符串常用函数方法如下:
方法 | 作用 |
---|---|
trim() | 移除字符串起始与结束的空格字符。 |
split() | 根据指定分隔符将字符串拆分为数组。 |
join() | 根据分隔符将 List 合并为字符串。 |
replace() | 替换字符串的指定字符为特定值。 |
replaceAll() | 替换字符串的所有指定字符为特定值。 |
public void stringDemo() {
String origin = "1, 2, 3, 4 ";
// Remove the head and last empty space
System.out.println("trim: " + origin.trim());
// Replace the char in string
System.out.println("replace: " + origin.replace(" ", ""));
// Split str to array
String[] array = origin.split(",");
System.out.println("split: " + Arrays.toString(array));
// Merge list to string
String joinStr = StringUtils.join(Arrays.asList(array), ",");
System.out.println("join: " + joinStr);
}
4. StringBuilder
StringBuilder
提供一种可变的字符串类型,速度快但非线程安全,适用于单线程。
- 操作少量的数据, 用
String
。 - 单线程操作大量数据,用
StringBuilder
(速度快,非线程安全,即异步)。 - 多线程操作大量数据,用
StringBuffer
(通过synchronized
加锁实现线程安全,但速度慢)。
public void Demo(){
StringBuilder builder = new StringBuilder();
builder.append("Hello");
builder.append("World!");
// insert a char at index 5
builder.insert(5, "J");
// replace a char at index 5
builder.setCharAt(5, ' ');
// delete char at specify index
builder.deleteCharAt(5);
String result = builder.toString();
System.out.println(result);
}
五、属性配置
1. 远程调试
在工程的 resources/bootstrap.conf
配置添加如下配置,并在代码中进行加载即可。
java.arg.debug=-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=8000
当然也可以值启动命令中添加 -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=8000
设置远程调试配置。
nohup java -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=8000 -jar my-application.jar> application-info.log 2>&1 &
完成上述配置之后在 IDEA
中启动配置中新增 Remote JVM Debug
并填写对应对应的服务 IP
与端口,同时选择对应的工程模块启动后即可实现远程 Debug
。
2. 参数配置
当工程中涉及到一些场景可变参数时可以通过外部文件配置属性,再由代码程序进行读取,实现更动态的管理。
在启动项的 VM Option
中添加如下内容,当然实际工程需要添加到对应的配置文件中。
代码中获取配置属性值如下:
public void demo() {
// Get all property
Properties property = System.getProperties();
System.out.println(property);
// Get specific property
String info = System.getProperty("properties.info");
System.out.println(info);
}