JDK和JRE
安装以及环境变量
https://www.oracle.com/java/technologies/javase-jdk14-downloads.html
尽量不要装在c盘,且目录全英文不要有空格。
找到环境变量,在path中添加刚才安装好的java文件夹中的bin目录的地址即可。
或者新建一个变量,叫JAVA_HOME:
然后:
数据类型
整型
浮点型
char型
char型里面必须放一个字符,但String型可以不放任何字符。
boolean型
这个目前为止没啥说的,就是true和false
强制类型转换精度问题
- 损失精度:
- 带小数->整型
- 128给byte(-128-127)会变成-128
- 多位小数->少位小数
long l = 1234输出并不会报错,因为你没加l它会默认是个int给了long,类型提升。
long l = 2135464613132514658,这种就不行了,因为这超出了int的范围。
float = 12.3会报错,因为你不加f默认是double,现在要给float自然会报错。
byte b = 10
byte b1 = b + 1 也会报错,因为b虽然是byte,但1默认是int,超出了byte b1的范围。
字符串类型 (不是基本数据类型)
char型里面必须放一个字符,但String型可以不放任何字符。
练习:
eclipse 常见问题
https://www.bilibili.com/video/BV1Kb411W75N?p=138
一维数组
public class HelloWorld {
public static void main(String[] args) {
System.out.println("hello world!");
// 静态初始化: 既初始化了数组又赋了值
int[] a = new int[]{1,2,3,4,5};
// 动态初始化: 只初始化了数组
String[] names = new String[5];
for (int i = 0; i < a.length; i++) {
System.out.print(a[i]);
}
}
}
默认初始化值
内存解析
一个题目 回形数
https://www.bilibili.com/video/BV1Kb411W75N?p=160
数组复制/赋值
如下代码说明了数组直接赋值给另一个变量并不会产生一个新的和原数组不相关的数组,对这个数组的操作是同步进行的(array1和array2)。
即array1和array2的地址值相同。
常用工具类
public class ArrayApi {
public static void main(String[] args) {
int[] arr = new int[]{1,2,3,4};
int[] arr2 = new int[]{6,3,1,4};
// 判断两数组是否相等(逐个元素比较)
boolean res = Arrays.equals(arr, arr2);
System.out.println(res);
// 输出数组信息
System.out.println(Arrays.toString(arr));
// 将指定的值填充到数组之中
Arrays.fill(arr, 100);
System.out.println(Arrays.toString(arr));
// 排序
Arrays.sort(arr2);
System.out.println(Arrays.toString(arr2));
// 二分查找
System.out.println(Arrays.binarySearch(arr2, 6));
}
}
对象
变量和属性(成员变量)
相同点
不同点:
方法
匿名对象
一般说来匿名对象只能调用一次,但通过下面的方式可以将一个匿名对象调用多次:
public class NiMingObject {
public static void main(String[] args) {
PhoneCentor p = new PhoneCentor();
p.show(new Phone()); // 匿名对象
}
}
class PhoneCentor {
public void show(Phone phone) {
phone.call();
phone.play();
}
}
class Phone {
int price;
public void call() {
System.out.println("打电话");
}
public void play() {
System.out.println("玩游戏");
}
}
重载
重载和函数名以及参数列表有关(同一个类中),与返回值类型无关。
若函数重载,则函数名必须相同,但参数列表要不同(包括参数类型和参数个数)
- 两函数名字相同,参数类型相同,但参数个数不同
- 两函数名字相同,参数类型不同,但参数个数相同
- 两函数名字相同,参数类型不同,参数个数也不同
- 两函数名字相同,参数类型相同(例如一个是String,int ; 另一个是int,String),这样也算重载,因为顺序不同,但参数类型都是(例如int)一种的情况下,这时调换顺序没有用,不算重载。
可变个数的形参
与本类中方法名相同,形参不同的方法构成重载。
方法参数的值传递机制
一道这种类型题:https://www.bilibili.com/video/BV1Kb411W75N?p=213
封装
一个类里有若干属性,比如说年龄、工资…很显然这些属性有的不能是负数或者不切实际的数。
因此我们再使用p.age这种形式赋值就不好了,所以要把这些属性设置成私有的,并搞一个方法专门用来为属性赋值,这就是属性的封装。
但以上只是封装性的一种体现,也可以将方法设置成私有的,比如说我有一堆其他排序方法是公有的,然后将交换两个数设置成私有的方法,只在类里面调用这个私有的,对外不暴漏接口。
还有单例模式,后面会有提到。
权限
可以看这个视频:https://www.bilibili.com/video/BV1Kb411W75N?p=275
构造器(构造方法)
先设计好类,然后通过构造器创建对象,再然后通过对象调用方法。
属性赋值的先后顺序
JavaBean
UML类图
this
当我们运行如下代码时,age最终的值是0而不是我们想要的10:
public class ThisText {
public static void main(String[] args) {
Person p = new Person();
p.setAge(10);
System.out.println(p.getAge());
}
}
class Person {
private String name;
private int age;
public Person() {
}
public void setAge(int age) {
age = age;
}
public int getAge() {
return age;
}
public void setName(String n) {
name = n;
}
public String getName() {
return name;
}
}
原因是在setAge方法中,age=age,编译器以为这两个age都是形参而不是属性。
加一个this.age = age就好了。
package
import
一个案例
https://www.bilibili.com/video/BV1Kb411W75N?p=250
继承
一旦子类继承父类,子类就获取了父类中生命的所有方法和属性。
即使父类声明为private的属性和方法,子类只是不能直接访问而已,但可以通过其他方法/接口来访问。
重写
说明:
- 为什么子类的权限修饰符要大于等于父类的权限修饰符?举个例子,Object中有hashCode方法,而且任意一个类都得有这个方法,如果子类重写父类方法时可以将权限修饰符变小,想象一下会发生什么?例如某个类A继承了Object他把hashCode方法重写为private,那么A的子类将无法看到A的hashCode方法,即子类不能重写父类中声明为private的方法。
还有另一个例子:https://blog.csdn.net/cbmljs/article/details/103870881 - 为什么父类中私有的方法不能被子类重写?因为私有的属性和方法只在本类内可见,所以子类根本看不到父类中私有的方法(但可以通过其他方法来访问或调用父类私有属性或方法),所以重写就无从谈起了。
super
子类重写父类的方法,可以被覆盖;但父类和子类都有的属性(同名)无法覆盖
super无法调用父类私有的属性和方法。
意思就是:
- 对于方法,如果子类重写了父类的方法,那么在子类中调用该方法只会运行它重写后的方法,这时如果想调用父类的方法就要用super;如果子类没重写父类的方法,那么你用this和super调用都行,因为this会先在本类中找,找不到再去父类找,而super会直接在父类找。
- 对于属性,父类有属性id,子类也有属性id,这不会产生覆盖,但在子类中直接使用id或this.id调用的是子类的id,如果想调用父类的id,就要用super
- 对于父类中有的属性/方法而子类中没有,则用不用super都行。
super调用构造器
如果父类的属性和方法是私有的,那子类是无法通过super访问到的, 但我们new子类对象需要父类的那些属性,使用setName之类的方法还太麻烦,所以想出了用带参数的构造器的形式,通过传参,把父类的那些私有化的属性交给super调用父类的构造器来完成:
public class Test {
public static void main(String[] args) {
Son son = new Son(1);
}
}
class Father{
private int num;
public Father() {
System.out.println("父类的无参构造器");
}
public Father(int num) {
System.out.println("父类的有参构造器" + num);
this.num = num;
}
private void run() {
System.out.println("father run ..." );
}
}
class Son extends Father {
public Son(int num) {
super(num);
// super.run(); 会报错,因为run方法在父类是私有的
System.out.println("子类的无参构造器");
}
}
这里需要注意的是,子类的构造器,不管是有参的还是无参的,默认都一定会访问父类的无参构造器(super())。而我们知道,如果你不指定构造器,会有一个默认的空参构造器,而一旦显示指明了有参构造器,默认提供的那个空参构造器就不会提供了。所以如果子类调用了super(),即父类的默认无参构造器,且父类没有无参构造器的话,你一定要手动的在父类指明一个,不然会报错,因为找不到父类的无参构造器,当然,要是子类的super调用的都是父类的有参构造器就不必再提供空参构造器了:
class Father{
private int num;
// public Father() {
// System.out.println("父类的无参构造器");
// }
public Father(int num) {
System.out.println("父类的有参构造器" + num);
this.num = num;
}
private void run() {
System.out.println("father run ..." );
}
}
class Son extends Father {
public Son() { // 注释掉了父类的空参构造器,会报错,因为找不到
System.out.println("子类的无参构造器");
}
}
父类:
public class Person {
String name;
int age;
public Person() {
}
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public void eat() {
System.out.println("eat!");
}
public void work(int distance) {
System.out.println("work!" + distance);
}
public double show() {
return 1.1;
}
}
子类:
public class Student extends Person{
String major;
int id = 12;
public Student() {
}
public Student(String major) {
this.major = major;
}
public Student(String name, int age, String major) {
this.name = name;
this.age = age;
this.major = major;
}
public void eat() {
System.out.println("学生吃饭!");
}
@Override
public double show() {
// TODO Auto-generated method stub
return super.show();
}
public void study() {
System.out.println("study: " + major);
}
}
我们想给学生对象赋值,注意学生类的构造方法,或许可以通过this.name = name直接赋值,但如果父类的属性是私有的呢?这么赋值显然是不行的。
this和super
如果一个构造器既没有写this,又没有写super,那么默认调用super。
this可以调用本类中的其他构造器,而上面说过,super构造器是一定会调的,但this和super又不能同时出现在构造器中,这不矛盾吗?
不矛盾,因为this虽然调了兄弟构造器,但兄弟构造器里也会自动调用super构造器!
重写的面试题
静态方法和私有方法能否被重写?
加了Override之后不能
https://www.bilibili.com/video/BV1TE41177mP?p=20
多态
声明了Person,然后把Man一个对象的地址赋值给给p2,那么再执行eat等方法时就会执行重写后的代码。但当我们ctrl+左键查看eat的位置时会发现跳转到了Person的eat方法中,也就是说编译和运行阶段是不一样的(编译看左面,运行看右面)。
但是!!! 多态对属性不适用,如果Person有id=1,Man有id=2,那么最后输出的时Person的id=1。
还有就是,子类继承父类,那么必定(先不说static)所有属性和方法都要有,都在堆区,只不过有些子类独有的属性和方法父类访问不到罢了。
还有就是如果Man子类中有父类没有的方法,那么p2访问不到。
多态的用处:https://www.bilibili.com/video/BV1Kb411W75N?p=281
子类可以重载父类方法!!!!
虚拟方法调用
多态就是在编译时无法确定父类引用到底指向哪个子类,那么如何证明这一点呢?可以看:https://www.bilibili.com/video/BV1Kb411W75N?p=283,4min左右。
instanceof
前面说过:
- 子类继承父类,那么必定(先不说static)所有属性和方法都要有,都在堆区,只不过有些子类独有的属性和方法父类访问不到罢了(因为变量声明为父类类型)。
那么如果才能访问这些访问不到的东西呢?把上面p2的类型转换一下就好了**(向下转型)**:
- Man m1 = (Man)p2;
这个m1存的内容还是上面代码中new出来的那个Man对象的地址,在数值上和p2是相等的,但是由于m2是Man类型,所以它就能用Man专属的方法。
但有时强转类型也会出现错误 ,比如Man和Woman都是Person的子类,那么Person类型转换成Man可以,但Man转Woman就不行。
向下转型
这种做法是不对的,因为如果可以正确,那么p4就能运行Man专属的方法了。而p4在new的时候堆区并没有Man专属的方法。
https://www.bilibili.com/video/BV1Kb411W75N?p=289
String str = new Date(),这是肯定不对的,编译都不能过
但我们可以略施手段让其编译通过,但仍然无法运行:
Object o = new Date();
String str = (String)o;
一道关于多态的练习题:https://www.bilibili.com/video/BV1Kb411W75N?p=290
注意,若要进行类型转换,等号两边必须是子父类的关系
object 类
==和equals
https://www.bilibili.com/video/BV1Kb411W75N?p=298
就是说:==比较的是两个变量存储的是不是用一个对象的引用,而equals,对与String、Date类来说,比较的是:两变量所指向的对象内容(属性是否一致),若一致则返回True,不再比较两变量是否是同一对象的引用。(因为String、Date重写了equals方法)
自定义类如何重写equals方法
就是说我们不想要objects原本的equals方法(比较是否为同一对象的引用),而是想比较两个实体对象的内容是否相同,像String那样。
https://www.bilibili.com/video/BV1Kb411W75N?p=297
注意
String s = “aa”;
String v = “aa”;
这种情况使用 s == v 返回的结果是True,使用equals的结果也是true,因为这么定义String是在常量池,因此只要是"aa"都返回同一个地址。
但String s = new String(“aa”) 和 String v = new String(“aa”) 再使用==就是false了,因为这是new出来的两个对象。
toString 方法
我们可以重写这个方法。
单元测试方法
包装类
我们希望基本数据类型也有类的一些特征:
基本类型转换为包装类
包装类转换成基本数据类型
基本数据类型可以做加减乘除,但却没有对象的那些方法;包装类有很多方法,但却不能直接做加减乘除,只能转换之后才可以。
自动装箱 && 拆箱
基本数据类型包装类与String的相互转换![在这里插入图片描述](https://img-blog.csdnimg.cn/20200913105450964.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzM5NTc5MDg3,size_16,color_FFFFFF,t_70#pic_center)
面试题
上面那个输出,因为三元运算符要求类型一致,所以int要变成double
下面那个就是1
static
static 内存解析
设计模式
单例模式
这么看太抽象,用代码理解:
对于上述代码,有一个Bank类,我们打算只创建一个对象,那么可以这样:
- 首先要把构造函数弄成private的,因为要是其它修饰符,在类外面就可以调用多次,这样就达不到要求了。
- 由于构造函数现在是private,所以要创建对象只能在Bank类内创建,于是 private Bank instance = new Bank();
- 这个在类内创建的对象其实就相当于一个属性了,所以我们打算弄一个get方法返回这个对象的引用
- 我们要想调用这个get方法就得有Bank类的对象,而构造函数已经是private了,即不可能在类外new到对象,于是只有将get方法弄成static的,这样在类外就可以通过类名直接调用了
- get方法是static的,那么它操纵的属性也得是static的(因为static加载在非static之前,即static不可能操纵在它之后出现的属性)
- 如此即可在类外:Bank bank = Bank.getIstance();且无论调用几次返回的都是指向同一个对象的引用。因为静态属性在类加载之初就已经确定了。
上述实现也叫饿汉式
下面介绍懒汉式
说明如下:
- 和饿汉式的区别在于null那里,懒汉式先不new对象,而是在调用get方法时才new
- 并用if判断,如果instance为null,说明此前没有创建过对象,如果不为null,说明已经创建过了,就不再创建新的而是把之前的返回
区别:
单例应用场景
main方法简介
https://www.bilibili.com/video/BV1Kb411W75N?p=329
代码块
代码块和构造函数的执行顺序
https://www.bilibili.com/video/BV1Kb411W75N?p=332
final
注意,这样的赋值是不可以的:
class Order {
final int age;
public void change(int n) {
age = n;
}
}
因为构造器是对象诞生前的最后一道关卡,也就是说在构造器结束之前所有的属性必须都得有值,而change方法显然是在构造器之后才能调用的,所以不行。
练习题
https://www.bilibili.com/video/BV1Kb411W75N?p=337
抽象类
注意点:
匿名类与匿名对象
https://www.bilibili.com/video/BV1Kb411W75N?p=347
模板方法的设计模式
https://www.bilibili.com/video/BV1Kb411W75N?p=348
接口
Java 7
注意,Java7之前接口中只可以定义全局常量和抽象方法,Java8之后有了新东西,我们先看前者:
抽象类和接口都不能实例化,但如果一个方法的形参声明为一个接口或者抽象类呢?
我们就得用其实现类/子类的对象。
下面我们来看一下上述描述的代码实现,接口的多态性:
public class InterfaceDuoTai {
public static void main(String[] args) {
Computer c = new Computer();
Printer p = new Printer("打印机a");
Headphone h = new Headphone("耳机a");
c.work(h);
}
}
class Computer {
public void work(USB usb) {
usb.start();
if (usb instanceof USB) {
System.out.println("****");
}
if (usb instanceof Printer) {
Printer p1 = (Printer)usb;
System.out.println(p1.name);
p1.stop();
}
else if (usb instanceof Headphone) {
System.out.println("----");
Headphone h1 = (Headphone)usb;
System.out.println(h1.name);
h1.stop();
}
}
}
interface USB{
void start();
void stop();
}
class Printer implements USB {
String name;
public Printer(String string) {
this.name = string;
}
@Override
public void start() {
System.out.println("打印机连接成功!");
}
@Override
public void stop() {
System.out.println("打印机已安全移除!");
}
}
class Headphone implements USB {
String name;
public Headphone(String name) {
super();
this.name = name;
}
@Override
public void start() {
System.out.println("耳机连接成功!");
}
@Override
public void stop() {
System.out.println("耳机已成功断开!");
}
}
说明:我们在Computer类中的work方法的形参处,设置了USB usb,但由于接口无法实例化,因此只能将实现了这个接口的类的对象传进来,这也是多态。
创建接口匿名类实现类的对象
public class InterfaceDuoTai {
public static void main(String[] args) {
Computer c = new Computer();
Printer p = new Printer("打印机a");
// 创建接口非匿名实现类的非匿名对象
// Headphone h = new Headphone("耳机a");
// c.work(h);
// 创建接口非匿名实现类的匿名对象
// c.work(new Printer("打印机b"));
// 创建接口匿名实现类的非匿名对象
// USB phone = new USB(){
//
// @Override
// public void start() {
// System.out.println("开始工作");
// }
//
// @Override
// public void stop() {
// System.out.println("结束工作");
// }
// };
// c.work(phone);
// 创建接口的匿名实现类的匿名对象
c.work(new USB() {
@Override
public void start() {
System.out.println("开始工作了");
}
@Override
public void stop() {
System.out.println("结束工作");
}
});
}
}
class Computer {
public void work(USB usb) {
usb.start();
if (usb instanceof USB) {
System.out.println("****");
}
if (usb instanceof Printer) {
Printer p1 = (Printer)usb;
System.out.println(p1.name);
p1.stop();
}
else if (usb instanceof Headphone) {
System.out.println("----");
Headphone h1 = (Headphone)usb;
System.out.println(h1.name);
h1.stop();
}
}
}
interface USB{
void start();
void stop();
}
class Printer implements USB {
String name;
public Printer(String string) {
this.name = string;
}
@Override
public void start() {
System.out.println("打印机连接成功!");
}
@Override
public void stop() {
System.out.println("打印机已安全移除!");
}
}
class Headphone implements USB {
String name;
public Headphone(String name) {
super();
this.name = name;
}
@Override
public void start() {
System.out.println("耳机连接成功!");
}
@Override
public void stop() {
System.out.println("耳机已成功断开!");
}
}
代理模式
要用文字解释这个很抽象,我们还是用代码来理解:
public class ProxyTest {
public static void main(String[] args) {
Proxy p = new Proxy(new Customer());
p.shopping();
}
}
interface Shop {
public void shopping();
public void weather();
public void money();
}
// 被代理类
class Customer implements Shop {
@Override
public void shopping() {
System.out.println("顾客购买商品");
}
@Override
public void weather() {
// TODO Auto-generated method stub
}
@Override
public void money() {
// TODO Auto-generated method stub
}
}
// 代理类
class Proxy implements Shop{
private Shop shop;
public Proxy(Shop shop) {
this.shop = shop;
}
@Override
public void shopping() {
weather();
money();
shop.shopping();
}
@Override
public void weather() {
System.out.println("代理查看天气");
}
@Override
public void money() {
System.out.println("代理准备钱");
}
}
程序运行结果如下:
对上述代码的说明:
- 我们显示定义了一个接口,然后又搞了一个什么代理类和一个被代理类,可以这么理解:接口就是你想做的事,例如你想去商场购物,被代理类就是你自己,但是你比较懒,只想去购物不想费事去看天气好不好、看钱包有没有钱…所以你想找个人帮你处理这些事,那个人就是代理类。
- 你和那个人都要实现接口,因为你只负责购物,所以其他方法都不用写,但那个人要实现其他方法,并且在shopping方法中要让你购物,而不是他购物,
- 所以在代理类中定义了一个Shop类型的属性,并弄一个构造器,利用多态将Customer的对象传到Proxy中(因为Customer类实现了Shop接口,作为其实现类可以使用多态),在shopping方法中可以调用shop属性(其实是Customer的一个对象),进而调用Customer的shopping方法。
- 整个过程并没有显示Customer对象调用shopping方法,体现了代理的作用。
工厂模式
先做简单了解:https://www.bilibili.com/video/BV1Kb411W75N?p=356
几道题
-
下面代码哪里有问题?
接口和父类都有同名属性,那么对于子类来说,直接调用x就不知道调的是谁了,只有这么调用才行:
-
下面代码哪里出问题了?
容易产生疑惑的地方:
- Ball实现了Playable和Bounceable两个接口,但这两个接口都有抽象方法play,那么在Ball里面实现的play会对这两个接口都起作用。
- Ball里面的play方法有错误,不能给ball赋新值,因为Rollable接口定义了全局常量ball,只是省略了 public static final 而已,所以ball的值不能再更改了。
接口 Java 8
现在我们可以在接口内定义静态方法和默认方法了:
注意:
- 接口中定义的静态方法,只能接口自己用,用实现类调不可以,用实现类的对象调也不可以。
- 接口中的默认方法要通过实现类的对象调用。
- 接口中的默认方法可以被实现类重写(去掉default)
- 如果实现类的父类和被实现的接口有同名方法 且 没有被子类重写,那么优先调用父类的重名方法(这和前面那道题不一样,前面那道题是说接口和父类有重名属性,那种情况下编译会报错?)
- 如果实现类实现了多个接口 且 这多个接口中有重名的方法,那么会报错,除非实现类重写这个重名的方法。
- 如果实现类重写了接口的方法,但是还想调用接口的方法,那么就要:接口名.super.方法名(); 或者重写了父类的方法但还想调用父类的方法,那么:super.方法名();
内部类
-
成员内部类
- 静态成员内部类
- 非静态成员内部类
-
局部内部类
更详细看:https://www.bilibili.com/video/BV1Kb411W75N?p=362
4.1:
4.2:
这个Bird是内部类,外面还有一个Person类,也有name属性。
4.3:
这个Comparable是在一个类里定义的:
https://www.bilibili.com/video/BV1Kb411W75N?p=365
当时没咋看懂,可以看视频好好想想。
总结: