JavaSE程序设计
参考资料
- 廖雪峰java
- Java基础:Java虚拟机(JVM)
- java程序设计精编教程第3版
- java 堆栈和常量池 https://cloud.tencent.com/developer/article/1453511
- java string的indexof方法 https://www.runoob.com/java/java-string-indexof.html
- 自己上课的一些记录和网上资料的融合,仅作参考
Java概述
- andorid工程师、大数据
- hadoop、一些编程细节
- 企业级应用
- SSM
Java特性
-
java8,java11长期支持
-
javaSE个人、javaEE针对web应用开发、javaME移动端
-
健壮性
-
跨平台,一个.class文件可以在各个平台运行
-
解释性,需要解释器解释才能执行
-
JVM,java虚拟机包含在jdk中
- 集成开发环境 IDE:Integrated Development Environment
- JDK(Java Development Kit)包含JRE
- JRE = Jvm(java虚拟机) + Java类库(Java SE)
Java_JDK安装
- Oracle官网
- 安装完成后配置环境变量,尽量在系统变量中添加绝对路径,或者可以在系统变量中添加JAVA_HOME(地址是Jdk的安装目录),其他的环境变量用的时候就引用%JAVA_HOME%。
- 安装的时候一定要注意,cmd窗口必须重启才能生效新的环境变量。
C:\Users\71041>java -version |
Java开发流程
- 用Sublime Text编写
//java入门 |
-
如果用windows的dos,只认GBK
-
java 执行时用类名,不加.class后缀
Java与C++基本语法的区别
输入输出
-
输出就是调用函数方法
public class Main {
public static void main(String[] args) {
System.out.print("A,");
System.out.print("B,");
System.out.print("C.");
System.out.println();
System.out.println("END");
}
}println
是print line的缩写,表示输出并换行。因此,如果输出后不想换行,可以用print()
: -
输入要先创建Scanner对象
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in); // 创建Scanner对象
System.out.print("Input your name: "); // 打印提示
String name = scanner.nextLine(); // 读取一行输入并获取字符串
System.out.print("Input your age: "); // 打印提示
int age = scanner.nextInt(); // 读取一行输入并获取整数
System.out.printf("Hi, %s, you are %d\n", name, age); // 格式化输出
}
} -
具体搜索Scanner类的的相关参数
判断相等
-
浮点类型
浮点数在计算机中常常无法精确表示,并且计算可能出现误差,因此,判断浮点数相等用
==
判断不靠谱,正确的方法是利用差值小于某个临界值来判断:public class Main {
public static void main(String[] args) {
double x = 1 - 9.0 / 10;
if (Math.abs(x - 0.1) < 0.00001) { //用小于某一临界值判断
System.out.println("x is 0.1");
} else {
System.out.println("x is NOT 0.1");
}
}
} -
引用类型
要判断引用类型的变量内容是否相等,必须使用
equals()
方法:public class Main {
public static void main(String[] args) {
String s1 = "hello";
String s2 = "HELLO".toLowerCase();
System.out.println(s1);
System.out.println(s2);
if (s1.equals(s2)) { //
System.out.println("s1 equals s2");
} else {
System.out.println("s1 not equals s2");
}
}
}
switch_case的不同
-
穿透性
使用
switch
时,注意case
语句并没有花括号{}
,而且,case
语句具有“穿透性”,漏写break
将导致意想不到的结果:public class Main {
public static void main(String[] args) {
int option = 2;
switch (option) {
case 1:
System.out.println("Selected 1");
case 2:
System.out.println("Selected 2");
case 3:
System.out.println("Selected 3");
default:
System.out.println("Not selected");
}
}
}
//结果:
Selected 2
Selected 3
endJava12的特殊写法,模式的写法,省略了break,且不具有穿透性
public class Main {
public static void main(String[] args) {
String fruit = "apple";
switch (fruit) {
case "apple" -> System.out.println("Selected apple");
case "pear" -> System.out.println("Selected pear");
case "mango" -> {
System.out.println("Selected mango");
System.out.println("Good choice!");
}
default -> System.out.println("No fruit selected");
}
}
} -
输入字符串是比较内容相等
当case中是字符串时,比较的是字符串的内容是否相等。
-
java12switch可返回值
public class Main {
public static void main(String[] args) {
String fruit = "apple";
int opt = switch (fruit) {
case "apple" -> 1;
case "pear", "mango" -> 2;
default -> 0;
}; // 注意赋值语句要以;结束
System.out.println("opt = " + opt);
}
}为了在default的中返回值还可
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);
}
}
for循环的不同
-
for each循环
public class Main {
public static void main(String[] args) {
int[] ns = { 1, 4, 9, 16, 25 };
for (int n : ns) {
System.out.println(n);
}
}和
for
循环相比,for each
循环的变量n不再是计数器,而是直接对应到数组的每个元素。for each
循环的写法也更简洁。但是,for each
循环无法指定遍历顺序,也无法获取数组的索引。for each
循环能够遍历所有“可迭代”的数据类型。 -
当用foreach来迭代访问数组元素时,foreach中的循环变量相当于一个临时变量,系统会把数组元素依次赋给这个临时变量,而这个临时变量并不是数组元素,它只是保存了数组元素的值。因此希望改变数组元素时,则不能使用这种foreach循环。
定义多维数组
//二维 |
-
输出可用Java标准库的
Arrays.deepToString()
:import java.util.Arrays;
public class Main {
public static void main(String[] args) {
int[][] ns = {
{ 1, 2, 3, 4 },
{ 5, 6, 7, 8 },
{ 9, 10, 11, 12 }
};
System.out.println(Arrays.deepToString(ns));
}
}
可传递命令参数
public class Main { |
javac Main.java |
多参数传递
- 传可变参数的时候,注意放到最后
public class Computer{ |
Java面向对象与C++的区别
Java类定义的规则
- 一个Java源文件可以包含多个类的定义,但只能定义一个public类,且public类名必须与文件名一致。如果要定义多个public类,必须拆到多个Java源文件中。
this指针访问规则
class Person { |
-
每个实例方法都有this,总是指向调用对象。
-
构造方法和实例方法都可以用。
-
类构造函数用this和不用this的区别https://blog.csdn.net/sc179/article/details/108006442
类的变量名是a,构造函数中形参名也是a,当形参名和成员变量名重名时,就会覆盖成员变量名。于是等号左边和右边的两个a都代表的是形式参数,给成员变量再赋值失败。
对象实例定义
Person ming = new Person(); |
可变参数的定义
class Group { |
- 可以传大于等于零的参数,要放到形参表的最后面
方法参数绑定
-
基本类型参数的传递,是调用方值的复制。双方各自的后续修改,互不影响。
-
引用类型参数的传递,调用方的变量,和接收方的参数变量,指向的是同一个对象。双方任意一方对这个对象的修改,都会影响对方(因为指向同一个对象嘛)。
public class Main {
public static void main(String[] args) {
Person p = new Person();
String bob = "Bob";
p.setName(bob); // 传入bob变量
System.out.println(p.getName()); // "Bob"
bob = "Alice"; // bob改名为Alice
System.out.println(p.getName()); // "Bob"还是"Alice"?
}
}
class Person {
private String name;
public String getName() {
return this.name;
}
public void setName(String name) {
this.name = name;
}
}注意:在java中String类型是不可变的,当发生改变时,会重新分配内存,即生成一个新的内存地址,例如:0x0022,此时p.bob的内存指向仍为0x0011,也就是Bob,所以输出的依旧是Bob。
重载的目的
-
重载也叫静态多态
子类重载父类也叫动态多态
-
方法重载的目的是,功能类似的方法使用同一名字,更容易记住,因此,调用起来更简单。
class Hello { |
另一个例子,着重表示重载在上下转型中的变化
class E_29{ |
继承
-
子类继承了超类的哪些内容,都不继承超类的构造函数。
-
子类最多只有一个超类。
-
第一种情况,在同一个包中,则子类继承,除了私有成员外的所有成员。(同一个包内都为友好类)
impot java.util.Scanner;
class People{
float weight,height;
void speak(String s){
System.out.println(s);
}
}
class Student extends People{
int number;
double add(double a,double b)
{
return a + b;
}
}
public class Example6_1{
public static void main(String args[]){
Student.zhangSan = new Student();
}
} -
第二种情况,不在一个包内,子类只继承超类的protected、public成员,但是没有继承的成员变量也要分配空间。
public class People{
int age,leg =2,hand = 2;
public void showPeopleMess(){
System.out.println( )
}
}
public class Student extends People{
int number;
public void study(){}
}
-
-
extends
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 {
// 不要重复name和age字段/方法,
// 只需要定义新增score字段/方法:
private int score;
public int getScore() { … }
public void setScore(int score) { … }
} -
Java只允许一个class继承自一个类
-
super的使用
super
关键字表示父类(超类)。子类引用父类的字段时,可以用super.fieldName
。- 还可以
super()
调用父类的构造函数,使用时要注意父类的构造形式。
-
阻断继承 Java15
-
类的向上转型
- 这种把一个子类类型安全地变为父类类型的赋值,被称为向上转型(upcasting)
-
类的向下转型
-
如果把一个父类类型强制转型为子类类型,就是向下转型(downcasting)
-
Java提供了
instanceof
操作符,可以先判断一个实例究竟是不是某种类型Person p = new Person();
System.out.println(p instanceof Person); // true
-
多态
成员变量的隐藏和方法重写
-
子类中声明的成员变量和超类中的成员变量同名时,子类就隐藏了继承的成员变量。
class C{
private int x= 4;
public String toString(){return ""+x;}
}
class D extends C{
private double x =3.5; //这里隐藏了超类的x
public String toString(){return ""+x;}
}
//super.x 调用隐藏的父类变量
//this.x 当前类的变量
Override
方法声明的两个组件构成了方法签名 ,即方法的名称和参数类型。
方法名相同,方法参数相同,但方法返回值不同,也是不同的方法。在Java程序中,出现这种情况,编译器会报错。
Override和Overload不同的是,如果方法签名不同,就是Overload,Overload方法是一个新方法;如果方法签名相同,并且返回值也相同,就是Override
。
-
类中添加@Override
加上
@Override
可以让编译器帮助检查是否进行了正确的覆写。希望进行覆写,但是不小心写错了方法签名,编译器会报错。public class Main {
public static void main(String[] args) {
}
}
class Person {
public void run() {}
}
public class Student extends Person {
// Compile error!
public void run(String s) {}
} -
多态具有一个非常强大的功能,就是允许添加更多类型的子类实现功能扩展,却不需要修改基于父类的代码。
-
调用super
class Person {
protected String name;
public String hello() {
return "Hello, " + name;
}
}
Student extends Person {
public String hello() {
// 调用父类的hello()方法:
return super.hello() + "!";
}
} -
final
-
继承可以允许子类覆写父类的方法。如果一个父类不允许子类对它的某个方法进行覆写,可以把该方法标记为
final
。用final
修饰的方法不能被Override
:class Person {
protected String name;
public final String hello() {
return "Hello, " + name;
}
}
Student extends Person {
// compile error: 不允许覆写
public String hello() {
}
} -
如果一个类不希望任何其他类继承自它,那么可以把这个类本身标记为
final
。用final
修饰的类不能被继承:final class Person {
protected String name;
}
// compile error: 不允许继承自Person
Student extends Person {
} -
对于一个类的实例字段,同样可以用
final
修饰。用final
修饰的字段在初始化后不能被修改。例如:class Person {
public final String name = "Unamed";
} -
可以在构造方法中初始化final字段:
class Person {
public final String name;
public Person(String name) {
this.name = name;
}
}
-
抽象类
//只有抽象方法无法编译 |
抽象方法
- 抽象类可以有抽象方法也可以没有
- 方法前必有abstract
- 实例方法才能是抽象方法,static不能是
- final也不能是抽象方法
面向对象编程的本质
- 上层代码只定义规范(例如:
abstract class Person
); - 不需要子类就可以实现业务逻辑(正常编译);
- 具体的业务逻辑由不同的子类实现,调用者并不关心。
接口与实现
-
抽象方法就是接口规范
-
接口内常量都是public、static、 final 均省略
-
接口内方法都是public、abstract ,均省略,不能为static、final
-
接口中没用构造方法,方法会抛出异常
-
如果一个抽象类没有字段,所有方法全部都是抽象方法,就可以把该抽象类改写为接口:
interface
。interface Person {
void run();
String getName();
} -
当一个具体的
class
去实现一个interface
时,需要使用implements
(实现)关键字。class Student implements Person {
private String name;
public Student(String name) {
this.name = name;
}
public void run() {
System.out.println(this.name + " run");
}
public String getName() {
return this.name;
}
}我们知道,在Java中,一个类只能继承自另一个类,不能从多个类继承。但是,一个类可以实现多个interface,例如:
class Student implements Person, Hello { // 实现了两个interface
...
} -
接口的继承
修饰符 interface 子接口 extends 多接口列表{
接口体
} -
抽象类和接口的对比
abstract class interface 继承 只能extends一个class 可以implements多个interface 字段 可以定义实例字段 不能定义实例字段 抽象方法 可以定义抽象方法 可以定义抽象方法 非抽象方法 可以定义非抽象方法 可以定义default方法 -
接口继承与普通类的继承相同使用extends
-
default方法
实现类可以不必覆写
default
方法。default
方法的目的是,当我们需要给接口新增一个方法时,会涉及到修改全部子类。如果新增的是default
方法,那么子类就不必全部修改,只需要在需要覆写的地方去覆写新增方法。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;
}
}
接口回调
- 可以把实现某一个接口的类创建的对象的引用赋给该接口类型的变量
- 接口类的对象可以调用子类的方法,这个方法是子类复写接口中的方法
动态多态
- 下面的c为接口类型的对象,可以调用不同子类的对象复写的接口方法,这就是动态多态。
class Y{ |
静态字段和方法
-
静态字段
-
实例字段在每个实例中都有自己的一个独立“空间”,但是静态字段只有一个共享“空间”,所有实例都会共享该字段。即修改一个实例的值,所有实例的值都改变。
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 class
的静态字段。所以,所有实例共享一个静态字段。因此,不推荐用实例变量.静态字段
去访问静态字段,因为在Java程序中,实例对象并没有静态字段。在代码中,实例对象能访问静态字段只是因为编译器可以根据实例类型自动转换为类名.静态字段
来访问静态对象。推荐用类名来访问静态字段。可以把静态字段理解为描述class
本身的字段(非实例字段)。
-
-
静态方法
-
类似C++的调用方式
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;
}
}
-
-
接口的静态字段
-
因为
interface
是一个纯抽象类,所以它不能定义实例字段。但是,interface
是可以有静态字段的,并且静态字段必须为final
类型public interface Person {
public static final int MALE = 1;
public static final int FEMALE = 2;
}
//简写为
public interface Person {
// 编译器会自动加上public statc final:
int MALE = 1;
int FEMALE = 2;
}
-
包package
-
解决命名冲突
-
包没有父子关系。java.util和java.util.zip是不同的包,两者没有任何继承关系。
-
包访问权限
-
定义
package 包名; -
包的结构
-
package tom.jiefei;
-
类必须在/tom/jiefei文件夹下
javac -d 目录 //指定生成的class文件存储的目录
-
源文件可以任意放,但是.class文件必须放到相应的包内
-
-
有命令如下
Java的上层文件夹为1000
C:\1000> javac tom.jiafei.主类名.java
C:\1000> java tom.jiafei.主类名
import
-
类要使用的类和自己不在一个包中。
import java.util.Date;
import java.javax.swing.*; -
不引入scanner如下
java.util.Scanner reader = new java.util.Scanner(System.in)
-
例子如下
import java.util.Date;
public class Example5_12{
public static void main(String args[]){
Date date = new Date();
System.out.println(date): //省略了toString()
}
} -
引入自定义包的所有类
import tom.jiefei.*;
-
例子
package tom.jiefei;
public class Triangle{
double sideA,sideB,sideC;
boolean isTriange;
public Triangle(double a double b,double c){
sideA = a;
sideB = b;
sideC = c;
if(a + b > c && a+c > b && c + b > a){
isTriange =true;
}
else{
isTriange = false;
}
}
public void area(){
if(isTriangle){
double p = ( sideA + sideB + sideC)/2.0;
double area = Math.sqrt(p*(p-sideA)*(p-sideB)*(p-sideC));
System.out.println("是一个三角形,面积是:"+area);
}else{
System.out.println("不是一个三角形,不能计算面积");
}
}
} -
如何找类引用的.class文件
- c:/ch5/tom/jiafei/Triangle.class
访问权限
- 成员的访问权限
- 由成员自身决定
- private --> protected -->public -->包访问权限
- 缺省的访问权限就是包访问权限
- 访问私有变量调用公有方法
- 受保护的,不在一个包的子类可以访问,同一个包内的其他类也可以访问。
权限 | 同类 | 同包 | 不同包子类 | 不同包非子类 |
---|---|---|---|---|
private | √ | |||
default | √ | √ | ||
protect | √ | √ | √ | |
public | √ | √ | √ | √ |
public类和友好类
- 同一个包的类中,能够用友好类创建对象
对象的组合
-
组合对象,子对象
-
大的对象可以包含另外一个或多个对象作为成员变量。
-
string是对象
实例成员与类成员
-
用关键字static修饰声明的变量称为类变量,否则称为实例变量。
-
类变量,是某一类的常用变量,是这一类共享的
public class Q{
public static void main(String[] args) {
B b1 = new B(),b2 = new B();
b1.setN(3);
b2.setN(5);
int s1 = b1.getSum();
int s2 = b2.getSum();
System.out.println(s1+s2);
}
}
class B{
int n;
static int sum = 0; //静态变量所有类共享
void setN(int n){
this.n = n;
}
int getSum(){
for(int i = 1;i <=n;i++)
{
sum = sum + i;
System.out.println(sum);
}
return sum;
}
} -
所有成员共享类变量,不同对象的成员变量不同。
实验1:equals、get、set
public class Point{ |
- 引用类型不能用 == 来判断相等,因为 java中== 判断的是引用的是否是同一对象,而不是值是否相等。应该使用equals()方法。参考:https://blog.csdn.net/nxj_climb/article/details/113175127
上转型对象
-
上转型对象可以访问子类继承或隐藏的成员变量,也可以调用子类继承的方法或子类重写的实例方法。
//向上转型的b只能访问到子类对象a的function
class 父类{
private int x = 4;
function();
}
class 子类 enxtends 父类
private int x = 3.5;
others();
function();
}
子类 a = new 子类();
父类 b = a;
//向下造型
子类 c = (子类)b;
c.function(); //此时调用的还是子类的函数
//辨析
父类 c = new 子类();
c.x//访问的是父类的x
父类 c = new 父类();
c.x//访问的是父类的x -
静态类型只对应本对象,动态类型需要看new的那个对象。
开闭原则
- 让设计的系统应当对扩展开放,对修改关闭。
- 面向接口编程的多态
组件和事件处理
java Swing
-
图形用户界面
-
JFrane窗口
-
javax.swing
组件和容器
- Component 类的子类或间接子类创建的对象为一个组件。
- container 的子类的间接或子类创建的一个对象为一个容器。
JFrame常用方法
import javax.swing.*; |
基本组件和容器组件布局
- 记住JPnael、JFrame、JTabbedPane、JDialog这几个容器的缺省布局分别是什么?分别是FlowLayout、BorderLayout、CardLayout、BorderLayout
内部类
- 内部类看作内部类的成员
- 外部类的类体中可以用内部类声明的对象,作为外部类的成员
- 内部类不能有类变量和类方法
- 外部类和内部类都产生.class文件,内部类有$符
事件处理
-
设计一个按钮引发的动作事件程序
-
一个按钮就是一个动作事件
import java.awt.event.ActionListenner; |
事件的委托处理模型
按钮触发动作事件
|
-
接口回调
addXXXListener(XXXListener listener)
-
监听者和事件源不要放在相同的类中,代码量小可以
MVC结构
- model
- view
- controller
Java核心类
string
-
String在java中也是一个类,Java编译器对
String
有特殊处理,即可以直接用"字符串"
来表示一个字符串。 -
实际上字符串在
String
内部是通过一个char[]
数组表示的,因此,按下面的写法也是可以的:String s2 = new String(new char[] {'H', 'e', 'l', 'l', 'o', '!'});
-
java的字符串是不可变的,类似c++的指针指向一个地址,创建了一个字符串该字符串就是固定的一段空间,指针指向该空间
-
Java中stirng == 判断的是是否是相同引用,并不是相同字符串,如果想判断相同的字符串,需要用equals方法判断。
-
要忽略大小写比较,使用
equalsIgnoreCase()
方法。 -
contains()搜寻字串
// 是否包含子串:
"Hello".contains("ll"); // true
"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(); // "Hello"
"\u3000Hello\u3000".strip(); // "Hello" //空格也会被移除
" Hello ".stripLeading(); // "Hello "
" Hello ".stripTrailing(); // " Hello" -
判断是否为空和空字符串
"".isEmpty(); // true,因为字符串长度为0
" ".isEmpty(); // false,因为字符串长度不为0
" \n".isBlank(); // true,因为只包含空白字符
" Hello ".isBlank(); // false,因为包含非空白字符 -
替换子串
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" -
分割字符串
传入的也是正则表达式
String s = "A,B,C,D";
String[] ss = s.split("\\,"); // {"A", "B", "C", "D"} -
拼接字符串
String[] arr = {"A", "B", "C"};
String s = String.join("***", arr); // "A***B***C" -
格式化字符串
字符串提供了
formatted()
方法和format()
静态方法,可以传入其他参数,替换占位符,然后生成新的字符串: -
这个部分需要的时候查询就好
反射
反射就是Reflection,Java的反射是指程序在运行期可以拿到一个对象的所有信息。
正常情况下,如果我们要调用一个对象的方法,或者访问一个对象的字段,通常会传入对象实例:
// Main.java |
注解
什么是注解(Annotation)?注解是放在Java源码的类、方法、字段、参数前的一种特殊“注释”:
// this is a component: |
多线程基础
- 多进程的缺点系统开销大,但一个进程奔溃不会影响其他进程。
- 多线程的缺点线程崩溃整个进程也会奔溃。
Random
- nextInt()
- Math类不能创建对象,System类不能创建其对象
内部类
- 内部类不能有类方法和类成员
匿名类
- 编译器会给内部类和匿名类名字
new <超类>|<接口>(){ |
异常类
- 异常类的对象
- Exception类
try{ |