文章目录
- 环境变量配置的意义
- javapath与classpath
- 伪随机数的生成
- Randmom
- Math
- ThreadLocalRandom
- SecureRandom
- java
- main函数
- Scanner 函数
- 打印函数
- 注释
- Java中的注释主要分为以下三种
- String
- boolean equals(Object anObject)
- int compareTo(String s)
- 数组
- 数组的遍历
- 数组名
- 数组参数
- 一些针对数组的函数
- Arrays.toString
- Arrays.copyOf
- Arrays.sort (int []a)
- 二维数组
- 类
- 方法
- this引用
- 构造函数
- 初始化
- 修饰符
- 访问修饰符
- 非访问修饰符
- final
- 封装扩展之包
- static
- 静态成员变量
- 静态成员函数
- 静态成员初始化
- 就地初始化
- 静态代码块初始化
- 实例代码块(构造代码块)初始化
- 内部类与外部类
- 获取内部类对象
- 内部类成员的访问
- 内部类的静态成员
- 内部类的特点
- 内部类的分类
- 普通内部类
- 静态内部类
- 局部内部类
- 匿名内部类
- 对象的打印
- 继承
- 构造函数
- super与this
- 初始化
- 访问
- 相同包中
- 不同包中
- 一个问题
- 继承方式
- final 关键字
- 继承与组合
- 多态
- 重写/隐藏
- 重载
- 向上转型
- 向下转型
- 动/静态绑定
- 多态
- 条件:
- 多态的优缺点
- 编译操作
环境变量配置的意义
- 软件程序的启动过程是os依据路径定位指定文件并加载到内存成为一个进程的过程
- os会首先依据系统变量的PATH中路径从上到下依次查询每个路径下的文件
javapath与classpath
JAVA_HOME的值就是 Java 所在的目录,一些Java软件和Java 的工具需要用到该变量,设置 PATH 和 CLASSPATH 的时候,也可以使用该变量以方便设置。
CLASSPATH直译过来是类路径,是Java环境配置中要设置的一个环境变量,就是.class文件的路径,表示JVM从哪里去寻找要运行的class文件,CLASSPATH = D:\java表示执行java命令时去D:\java目录中去找需要被执行的class文件并运行。
伪随机数的生成
Randmom
Random 对象在种子数相同的情况下,相同次数生成的随机数是相同的。比如两个种子数相同的 Random 对象,第一次生成的随机数字完全相同,第二次生成的随机数字也完全相同。默认情况下 new Random() 使用的是当前纳秒时间作为种子数的。
nextInt(int range );//生成0到range-1的整数
import java.util.Random;
// 生成 Random 对象
Random random = new Random();
for (int i = 0; i < 10; i++) {
// 生成 0-9 随机整数
int number = random.nextInt(10);
System.out.println("生成随机数:" + number);
}
Math
Math.random(),此方法会产生一个 0 到 1 的 double 值,因此需要用到整数的话使用与10进行积,进行强制转换即可。
ThreadLocalRandom
和线程安全有关,暂时没学
SecureRandom
和线程安全有关,暂时没学
java
main函数
public class HelloWorld{
public static void main(String[] args){
System.out.println("Hello,world");
}
}
在一个源文件中只能有一个public修饰的类,而且源文件名字必须与public修饰的类名字相同。
Scanner 函数
Scanner scanner = new Scanner(System.in);
int a = scanner.nextInt();
打印函数
System.out.println(Integer.MAX_VALUE);
注释
Java中的注释主要分为以下三种
- 单行注释:ctrl +/
多行注释:ctrl + shift+/
文档注释: /**- 反撤销重复操作
String
String是引用类型,内部并不存储字符串本身 ,而是存放堆空间的首地址
直接使用‘’==‘’,比较的是地址,比较内容使用equals或者compareTo
boolean equals(Object anObject)
按照字典序比较,相同返回true,反之false,对比strcmp
int compareTo(String s)
只比较最短长度次,如果到极限就返回剩余长度,否则就不相等字符的asicc码值差
数组
T[] 数组名 = new T[N];
T:表示数组中存放元素的类型
T[]:表示数组的类型
N:表示数组的长度
new:代表在堆上建立空间
数组的遍历
在数组中可以通过 数组对象.length 来获取数组的长度
for-each
```java int[] array = {1, 2, 3}; for (int x : array) { System.out.println(x); } ```
数组名
- 数组名是一个存放堆上地址的引用,存放的地址可以更改,类似指针,但是C++的引用一但引用就不可更改
数组参数
参数传基本数据类型 ,值拷贝
参数传数组类型(引用数据类型 ),外部和内部指向对象一致
作为函数返回值是一种引用,数据不会栈帧销毁
一些针对数组的函数
Arrays.toString
数组转字符串
import java.util.Arrays
int[] arr = {1,2,3,4,5,6};
String newArr = Arrays.toString(arr);
System.out.println(newArr);
// 执行结果
[1, 2, 3, 4, 5, 6]
Arrays.copyOf
- 数组拷贝,拷贝不用于引用的赋值
- 全拷贝(int [] a,int length)
- 范围拷贝(int []a ,int L,int R)
import java.util.Arrays;
public static void func(){
// newArr和arr引用的是同一个数组
// 因此newArr修改空间中内容之后,arr也可以看到修改的结果
int[] arr = {1,2,3,4,5,6};
int[] newArr = arr;
newArr[0] = 10;
System.out.println("newArr: " + Arrays.toString(arr));
// 使用Arrays中copyOf方法完成数组的拷贝:
// copyOf方法在进行数组拷贝时,创建了一个新的数组
// arr和newArr引用的不是同一个数组
arr[0] = 1;
newArr = Arrays.copyOf(arr, arr.length);
System.out.println("newArr: " + Arrays.toString(newArr));
// 因为arr修改其引用数组中内容时,对newArr没有任何影响
arr[0] = 10;
System.out.println("arr: " + Arrays.toString(arr));
System.out.println("newArr: " + Arrays.toString(newArr));
// 拷贝某个范围.
int[] newArr2 = Arrays.copyOfRange(arr, 2, 4);
System.out.println("newArr2: " + Arrays.toString(newArr2));
}
Arrays.sort (int []a)
数组排序,默认从小到大排序
二维数组
数据类型[][] [] [] 数组名称 = new 数据类型 [ 行数 ] [ 列数 ] { 初始化数据 };
int[][] arr = {
{1, 2, 3, 4},
{5, 6, 7, 8},
{9, 10, 11, 12}
};
for (int row = 0; row < arr.length; row++) {
for (int col = 0; col < arr[row].length; col++) {
System.out.printf("%d\t", arr[row][col]);
} S
ystem.out.println("");
}
类
- 用类类型创建对象的过程,称为类的实例化,在java中采用new关键字,配合类名来实例化对象。
- 类的实例化是在堆上开辟空间,只为属性开辟空间,方法是放在了一个代码段内,调用时进行栈帧
- 所有的类都·是一种引用数据类型,注意赋值
方法
public void fun();//不用加static
this引用
- this引用是方法的第一个隐含的对象引用,默认传调用对象的引用
- 在"成员方法"中,this只能引用当前对象,不能再引用其他对象,具有final属性
- 可以通过this(…)访问构造函数,但必须是第一句
构造函数
没有构造函数时,编译器会提供一个默认的构造函数,对属性进行默认赋值
没有返回值类型,设置为void也不行
初始化
对于默认构造函数,编译器会对属性进行默认赋值
代码编译完成后,编译器会将所有给成员的就地初始化的这些语句添加到所有构造函数中 。
对于无参的构造函数也要通过new
修饰符
访问修饰符
非访问修饰符
final
常量修饰符
封装扩展之包
- 为了更好的管理类,把多个类收集在一起成为一组,称为软件
包。- 包是对类、接口等的封装机制的体现,是一种对类或者接口等的很好的组织方式,比如:一个包中的类不想被其他包中的类使用。包还有一个重要的作用:在同一个工程中允许存在相同名称的类,只要处在
不同的包中即可- 除protect修饰的,同一个包中的不同类可以随意访问
static
静态成员变量
- Java中static只用于修饰类的成员和方法
- static修饰的成员变量,称为静态成员变量
- JDK7及以前,HotSpot(Java虚拟机)中存储在方法区,JDK8及之后,类变量存储在Java堆中
- 既可以通过对象访问,也可以通过类名访问,但一般更推荐使用类名访问 ,因为静态成员变量不在类对象中,而在方法区中,用类名访问更好。
- 生命周期伴随类的一生(即:随类的加载而创建,随类的卸载而销毁
静态成员函数
- Java中,被static修饰的成员方法称为静态成员方法,是类的方法,不是某个对象所特有的.可以通过对象调用,也可以通过类名.静态方法名(…)方式调用,更推荐使用后者
- 静态成员一般是通过静态方法来访问的。
- 静态方法没有隐藏的this引用参数,因此不能在静态方法中访问任何非静态成员变量 ,也不能调用任何非静态方法
- 静态方法无法重写,不能用来实现多态
静态成员初始化
静态成员变量一般不会放在构造方法中来初始化,构造方法中初始化的是与对象相关的实例属性
就地初始化
在定义时直接给出初始值
静态代码块初始化
静态代码块在类加载前就要执行
为类中的静态成员进行初始化使用的代码块称为静态代码块,且静态代码块只会执行一次
如果一个类中包含多个静态代码块,在编译代码时,编译器会按照定义的先后次序依次合并,最终放在生成的<>方法中,该方法在加载类时调用,并且只调用一次
实例代码块(构造代码块)初始化
实例代码块在加载对象时执行
非静态成员初始化使用构造代码块。实例代码块优先于构造方法执行,因为编译完成后,编译器会将实例代码块中的代码拷贝到每个构造方法第一条语句前。
所有的代码块总是优于其它代码先被执行,如:构造函数,普通函数
内部类与外部类
- 当一个事物的内部,还有一个部分需要一个完整的结构进行描述,而这个内部的完整的结构又只为外部事物提供服务,那么整个内部的完整结构最好使用内部类。在 Java 中,可以将一个类定义在另一个类或者一个方法的内部,前者称为内部类,后者称为外部类。内部类也是封装的一种体现。
- 在外部类中,内部类定义位置与外部类成员所处的位置相同,因此称为成员内部类
public class OutClass {
class InnerClass{
}
} /
/ OutClass是外部类
// InnerClass是内部类
获取内部类对象
//外部类
OuterClass oc=new OuterClass();
//内部类,2种方式都可以
OuterClass.InterClass ic=oc.new InterClass();
OuterClass.InterClass ic1=new OuterClass().new InterClass();
内部类成员的访问
OuterClass.InterClass._data6=20;
内部类的静态成员
- jdk8之前,内部类是不允许定义static成员的,如果非要定义,需要static final变为常量,编译阶段就完成内存的开辟;jkd20之后是可以定义static成员的
- 普通类和内部类的加载过程是不一样的,不能用那个一般流程看内部类加载
- 最关键的:外部类加载的时候,假设内部类有static成员,外部类加载的时候也会初始化这个static成员,但是内部类还未加载,所以不能定义static
内部类的特点
在内部类中可以直接访问外部类中:任意访问限定符修饰的成员 ,类似C++的内部友元类。
外部类不可访问内部类,只能通过内部类接口访问
如果外部类和内部类中具有相同名称成员时,优先访问的是内部类自己的 ;如果要访问同名的外部类成员时候,必须:外部类名称.(this).同名成员名字 ,this依据静态还是非静态添加
```java //非静态_data3; System.out.println(OuterClass.this._data3); //静态_data6 System.out.println(OuterClass._data6); ```
- 普通内部类与外部类成员变量位置相同,因此也受public、private等访问限定符的约束 ,因此普通内部类对象必须在先有外部类对象前提下才能创建
```java private class InterClass {} ```
. 普通内部类的非静态方法中包含了一个指向外部类对象的引用
外部类中,不能直接访问内部类中的成员,如果要访问必须先要创建外部类的对象
```java class OuterClass { public int _data1=1; public int _data2=2; public int _data3=3; public static int _data6 = 6; public OuterClass() { System.out.println("OuterClass"); } public void test() { InterClass i1=new InterClass(); i1.func(); System.out.println("OuterClass:test()"); } ```
内部类的分类
普通内部类
未static修饰的成员内部类
静态内部类
static修饰的成员内部类
static class InterClass2
静态内部类地位和外部类的静态成员地位一样,因此访问外部类成员不需要this指针,因此在内部类中只能访问外部类中的静态成员
创建内部类对象时,因为属于外部类,不需要先创建外部类对象
```java OuterClass2.InterClass2 ic=new OuterClass2.InterClass2(); ```
- 成员内部类,经过编译之后会生成独立的字节码文件,命名格式为:==外部类名称$内部类名称.class ==
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xXYltVUP-1689334214186)(./Java%E5%88%9D%E8%AF%86.assets/image-20230713180712023-1689242833401-1.png)]
局部内部类
public class OutClass {
int a = 10;
public void method(){
int b = 10;
// 局部内部类:定义在方法体内部
// 不能被public、static等访问限定符修饰
class InnerClass{
public void methodInnerClass(){
System.out.println(a);
System.out.println(b);
}
} /
/ 只能在该方法体内部使用,其他位置都不能用
InnerClass innerClass = new InnerClass();
innerClass.methodInnerClass();
}
public static void main(String[] args) {
// OutClass.InnerClass innerClass = null; 编译失败
}
}
局部内部类只能在所定义的方法体内部使用
不能被public、static等修饰符修饰
编译器也有自己独立的字节码文件,命名格式:外部类名字$x内部类名字.class,x是一个整数。
几乎不会使用
匿名内部类
对象的打印
利用重写规则,变量打印对象的成员内容
public static String valueOf(Object obj) {
return (obj == null) ? "null" : obj.toString();
}
public String toString() {
return getClass().getName() + "@" + Integer.toHexString(hashCode());
}
printIn会调用toString(),因此重写toString即可完成打印。
@Override//注解符:用于检测是否严格按照重写成功,可以省略
public String toString() {
return "InterClass2{" +
"_data4=" + _data4 +
", _data5=" + _data5 +
'}';
}
}
/**
* @author Administrator
*/
public class Test2 {
public static void main(String[] args) {
//与静态成员地位一样,因此这里使用类名定义,而不需要建立一个外部类
OuterClass2 oc2 = new OuterClass2();
System.out.println(oc2);
OuterClass2.InterClass2 ic = new OuterClass2.InterClass2();
System.out.println(ic);
}
继承
-
通过关键字extend继承主要解决的问题是:共性的抽取,实现代码复用。
-
Java不支持多继承,多继承可能有访问的二义性,提高代码冗余度,C++
在发生二义性的地方进行virtual继承解决多继承问题
-
就近原则访问:自己有优先自己的,如果没有则向父类中找 ;对于发生重载的地方,编译器会自动识别
-
super是一种只访问父类继承的可访问的
非静态属性与函数的关键字
,一般用于同名非静态属性或者同名非静态函数
;this的范围比super更大,父子类都可以访问,但是遵循就近原则。一种错误的说法:super是父类的引用
构造函数
Java子类的构造函数思想同C++一样
对于派生类对象调用构造函数时,先调用父类构造函数
如果父类是无参或者默认构造函数时,子类只需要考虑自己属性的初始化
如果父类是有参构造函数,子类的构造函数必须显示的给父类构造函数传参,否则编译错误;
通过super(…)调用父类构造,但是该语句必须是子类构造函数中第一条语句且super(…)只能在子类构造方法中出现一次,并且不能和this同时出现 ,构造函数只需要调用一次就够了
class Dog extends Animal { public String _color; public Dog() { super("小白", "小明");//放在第一行 System.out.println(" public Dog()"); }
super与this
- this是当前对象的引用,当前对象即调用实例方法的对象,super相当于是子类对象中从父类继承下来部分成员的引用
- 在非静态成员方法中,this用来访问本类的方法和属性,super用来访问父类继承下来的方法和属性
- this是非静态成员方法的一个隐藏参数,super不是隐藏的参数
- 成员方法中直接访问本类成员时,编译之后会将this还原,即本类非静态成员都是通过this来访问的;在子类中如果通过super访问父类成员,编译之后在字节码层面super实际是不存在的(通过字节码文件可以验证)
- 在构造方法中:this(…)用于调用本类构造方法,super(…)用于调用父类构造方法,两种调用不能同时在构造方法中出现
- 构造方法中一定会存在super(…)的调用, 用户没有写编译器也会增加,但是this(…)用户不写则没有
初始化
- 静态代码块总是被编译器提前集中并最早执行。父类静态代码块优先于子类静态代码块执行,且是最早执行
- 父类实例代码块和父类构造方法紧接着执行
- 子类的实例代码块和子类构造方法紧接着再执行
- 第二次实例化子类对象时,父类和子类的静态代码块都将不会再执行
Person:静态代码块执行
Student:静态代码块执行
Person:实例代码块执行
Person:构造方法执行
Student:实例代码块执行
Student:构造方法执行
===========================
Person:实例代码块执行
Person:构造方法执行
Student:实例代码块执行
Student:构造方法执行
访问
在类和对象章节中,为了实现封装特性,Java中引入了访问限定符,主要限定 :类或者类中成员能否在类外或者其他包中被访问。
相同包中
- 父类中protected成员在相同包子类中可以直接访问
- 父类中默认访问权限(default)修饰的成员在相同包子类中可以直接访问
- private成员不可访问
- public任何位置访问
不同包中
- 父类中protected修饰的成员在不同包子类中可以直接访问
- 父类中public修饰的成员在不同包子类中可以直接访问
- 父类中默认访问权限修饰的成员在不同包子类中不能直接访问
一个问题
为什么外部类的权限只有public和default2种控制符?
类的存在是要么是给外部调用,要么是内部自己调用,因此
- 对于外部类,要么是同包,要么是任何位置访问,因此是default和public
- 对于内部类有4种:任何位置(public),同包(default),父子(protected),不能访问(private).
继承方式
时刻牢记, 我们写的类是现实事物的抽象. 而我们真正在公司中所遇到的项目往往业务比较复杂, 可能会涉及到一系列复杂的概念, 都需要我们使用代码来表示, 所以我们真实项目中所写的类也会有很多. 类之间的关系也会
更加复杂.但是即使如此, 我们并不希望类之间的继承层次太复杂. 一般我们不希望出现超过三层的继承关系. 如果继承层次太多, 就需要考虑对代码进行重构了.如果想从语法上进行限制继承, 就可以使用 final 关键 .
final 关键字
final关键可以用来修饰变量、成员方法以及类
修饰变量或字段,表示常量**(即不能修改)**
修饰类:表示此类不能被继承( String 字符串类, 就是用 final 修饰的, 不能被继承 )
修饰方法:表示该方法不能被重写(后序介绍)
继承与组合
和继承类似, 组合也是一种表达类之间关系的方式, 也是能够达到代码重用的效果。组合并没有涉及到特殊的语法(诸如 extends 这样的关键字), 仅仅是将一个类的实例作为另外一个类的字段 。
继承表示对象之间是is-a的(包含的)关系,比如:狗是动物,猫是动物
组合表示对象之间是has-a的关系,比如:汽车
多态
重写/隐藏
-
重写是对父类中同名函数内容进行隐藏,子类重新写入新的内容,是为多态服务的。对老手机的功能,我们不能重新写,但可以继承,对新的类进行重写即可。
-
避免在基类的构造函数中调用重写的构造函数,可能引发null,或者子类对象未建立就访问其内容
-
子类的方法访问权限要大于等于父类的访问权限
-
三同原则:返回值类型(返回值构成父子关系也行),函数名,参数个数及顺序一样
-
private修饰的方法不能被重写;final修饰的方法也不能被重写;被static修饰也不可被重写
-
构造方法不可重写
-
重写的方法, 可以使用 @Override 注解来显式指定. 有了这个注解能帮我们进行一些合法性校验. 例如不小心将方法名字拼写错了 (比如写成 aet), 那么此时编译器就会发现父类中没有 aet 方法, 就会编译报错, 提示无法
构成重写
重载
允许对类内部的方法进行重载,也可以对继承中子类与父类同名函数进行重载
向上转型
- 实际就是创建一个子类对象,将其当成父类对象来使用 。也即C++的赋值兼容(切割),是安全的。
父类类型 对象名 = new 子类类型()
- 优点:让代码实现更简单灵活。
缺陷:不能调用到子类特有的方法 - 函数传参是允许向上转型的
向下转型
- 子类对象对父类对象进行引用
- 向下转型用的比较少,而且不安全,万一转换失败,运行时就会抛异常。Java中为了提高向下转型的安全性,引入了 instanceof ,如果该表达式为true,则可以安全转换
if(animal instanceof Cat){
cat = (Cat)animal;
cat.mew();
}
动/静态绑定
静态绑定:也称为前期绑定(早绑定),即在编译时,根据用户所传递实参类型就确定了具体调用那个方法。典型代表函数重载。
动态绑定:也称为后期绑定(晚绑定),即在编译时,不能确定方法的行为,需要等到程序运行时,才能够确定具体调用那个类的方法。
多态
- 本质是看this引用究竟是对基类还是派生类对象的引用
- 只有是基类对象的应用,编译器都会当作多态进行处理,只是看条件是否达成
条件:
必须在继承体系下
子类必须要对父类中方法进行重写
通过父类的引用调用重写的方法
多态的优缺点
- 能够降低代码的 “圈复杂度”, 避免使用大量的 if - else
什么叫 “圈复杂度” ?
圈复杂度是一种描述一段代码复杂程度的方式. 一段代码如果平铺直叙, 那么就比较简单容易理解. 而如果有很多的条件分支或者循环语句, 就认为理解起来更复杂.因此我们可以简单粗暴的计算一段代码中条件语句和循环语句出现的个数, 这个个数就称为 “圈复杂度”.如果一个方法的圈复杂度太高, 就需要考虑重构.不同公司对于代码的圈复杂度的规范不一样. 一般不会超过 10 .
-
可扩展能力更强
-
多态缺陷:代码的运行效率降低
编译操作
1. out目录下
- cmd
- javap -c +目标文件