Open QiYongchuan opened 4 months ago
Java中的基本数据类型
byte: 8位有符号的补码整数,范围为-128到127。 short: 16位有符号的补码整数,范围为-32768到32767。 int: 32位有符号的补码整数,范围为-2^31到2^31-1。 long: 64位有符号的补码整数,范围为-2^63到2^63-1。 float: 单精度32位IEEE 754浮点数。 double: 双精度64位IEEE 754浮点数。 boolean: 逻辑类型,只能取两个值之一:true或false。 char: 单个16位Unicode字符,范围为'\u0000'到'\uffff'。
整数:byte、short、int、long 浮点数:float、double 字符:char 逻辑:boolean
基本数据类型的默认值如下:
byte: 0 short: 0 int: 0 long: 0L float: 0.0f double: 0.0d boolean: false char: '\u0000'
补充:所有的引用类型则是null
数据类型转换
public static void main(String[] args) {
int num = 'a'; // char --> int 97
double d1 = 80; // double --> 80.0
System.out.println(num);
System.out.println(d1);
}
注: 1.混合多种类型时,转换为最高的类型 2.(byte,short)和char之间不会相互自动转换 3.byte,shoort,char 他们三者可以计算,计算时首先转化为int 再计算;另三种无论是单独还是混合都会提升。(精度提升到int)
强制类型转化:
数据溢出造成不可知的数
int a = (int)1.9;
System.out.println(a); //1 直接丢失了小数部分
int n = 2000;
byte b = (byte)n; // 超出byte127的范围,会造成数据溢出
System.out.println(b); //-48
基本数据类型与String类型的转换
OOP 面向对象编程
面向过程:适合简单、不需要协作的事务,重点关注如何执行
类比,如何把大象装进冰箱,1、2、3步,一步一步的执行,不需要过多的协作,一步一步就可以了
面向对象:宏观上把握,整体上分析整个系统;但具体到实现部分的操作的时候,仍然需要一步一步的考虑,也就是面向过程了。
两者是相辅相成,不可分开的:
具体概念
对象和类:类class是模板,是图纸,对象object(instance实例),对象是具体的体现,是实例。
类有三个成员:属性(field,也称为成员变量)、方法method、构造器constructor
属性即静态的数据,方法是动态的行为
属性(file 成员变量):用于定义该类或该类的对象包含数据,或者说静态特征,作用范围是整个类体,在定义成员变量时可以对其初始化,如果不对其初始化,java采用默认值对其初始化。
方法:用于定义该类或该类实例(对象)的行为特征和功能实现的。方法是类和对象行为特征的抽象。 因为在面向对象中,整个程序的基本单位是类,所以方法是从属于类和对象的。
public class Student {
int id;
int age;
String name;
public void study(){
System.out.println("I am studying");
}
public void sleep(){
System.out.println("I am sleeping");
}
//实现一个对象,实例化一个对象,利用的是编译器自动生成构造函数 Study(){}
public static void main(String[] args) {
Student st1 = new Student();
st1.id = 100;
st1.age = 20;
st1.name = "John";
st1.study();
st1.sleep();
}
}
简单内存分析(帮助理解面向对象):
构造方法(构造器constructor) why ?
在创建的同时进行赋值 如果不写,则是默认的系统指定的无参构造器,无法在创建的时候进行赋值,只能等创建完成之后,再赋值;一旦构建了有参构造器后,默认的构造器就会被覆盖了,如果想用就必须再创建一个默认的无参构造器。
class Person{ String name; int age; public Person(String name1,int age1){ System.out.println("Constructor 被调用"); name = name1; age = age1; } public Person(){ System.out.println("默认构造函数被调用"); } }
构造器用于对象的初始化,而不是创建;
构造方法是装修房子,而不是创建房子
创建对象的四步:
构造器(构造方法)的四个要点:
public class Point {
double x,y;
Point(double i, double j) {
x = i;
y = j;
}
构造方法重载
public class User {
int age;
String name;
int pswd;
public User(int age, String name, int pswd)
{
this.age = age;
this.name = name;
this.pswd = pswd;
}
public User()
{
}
对象创建流程详解:
JVM虚拟机内存模型
注意这里:
People p2 = p1 //执行的是将p1堆中的地址给了p2,
而
People p2 = new People() 则是实现了在堆中新建一个地址区
public static void main(String[] args) {
People p1 = new People();
p1.age = 10;
People p2 = p1;
System.out.println(p2.age); //10
p2.age = 20;
System.out.println(p1.age); //20
}
本质:两者是同一个引用地址
垃圾回收:
为什么 b.age 会报错?
将b设置为null,断开了b与Person对象的连接。在Java中,设置为null意味着引用不再指向任何对象,但由于a仍然指向那个对象,所以那个对象不会被垃圾回收器回收。
尝试打印b.age,但是会抛出NullPointerException,因为b已经被设置为null,不再指向任何对象。
System.out.println(b.age); // 抛出NullPointerException
区分:
这里p=null指向空,栈中test200中的p空了,但是main中的p.age依然指向原来的地址(堆中的地址p)
作用域 变量的范围。 Java中变量一般分为:属性和成员方法中的变量,即分别是全局变量和局部变量。
补充:
区别:
修饰符:
this关键字 why? 区分当前类的属性和局部变量而引入。
//访问成员方法的语法 this.f
class T{
public void f1(){
System.out.println("f1() 方法");
}
public void f2(){
System.out.println("f2() 方法");
//调用本类的f1()方法
//第一种方式
f1();
//第二种方式
this.f1();
}
}
构造器中使用this 注:仅限在构造器中使用,且this要放在第一条语句
this访问属性的区别: 如果没有this时,在成员方法中访问属性时采用就近原则(即有局部变量先访问局部变量,没有时访问全局变量),但是有this时,就只访问全局变量了。
答案10,9,10 涉及匿名对象,创建之后就销毁了 以及count++ 是后加加,先输出之后,后加
包 package why? 包的三大作用:
本质:创建不同的文件夹/目录,来保存文件
访问修饰符 用于控制方法和属性(成员变量)的访问权限(访问范围)。
封装 面向对象的三大特征:封装、继承、多态 封装:把抽象出来的数据(属性)和对对象的操作(方法)封装在一起,数据被保护在内部,程序的其它部分只有通过被授权的操作(方法),才能对数据进行操作。
如何实现?
构造器与setxx
因为当只写构造器时,可以在初始化时直接赋值,这样就绕过了set设置的规则;此时如果想继续保证set的效果,就将构造器与set结合,在构造器中调用set,这样即使是初始化赋值给的值不符合要求,就无法绕过set了。
继承 why ? 当两个类的属性和方法很多是相同的时候,怎么办 ==》继承,提高代码复用性 、
好处:
继承细节点:
子类继承了所有的属性和方法,但是私有属性和方法不能在子类中直接访问,只能通过公共的方法去访问。
private int n4 = 4;
public int getN4() {
return n4;
}
private void Test4(){
System.out.println("Private Base Test3...");
}
public void callPrivate(){ // 私有方法也可以使用公共方法调用
Test4();
}
创建子类对象时,必须调分类的构造器,先完成父类的初始化
在子类创建时,子类的构造器默认调用了super()方法:本质是调用父类的无参构造器。
*当创建子类时,无论调用子类的哪一个构造器,默认情况下总会调用父类的无参构造器;
java中所有类都是Objece类的子类,Object类是所有类的基类(父类) 父类构造器的调用不限于直接的父类,将会一直往上追溯到Object类(顶级父类)
单继承机制,只能一个父类;如果想继承C,可以先让B继承C,A就继承到了C.
继承的本质 ==>逐层向上的查找关系
关键: 不要忘记了所有的构造器都有默认的super() super("hhh")显示调用,并传参
supper关键字 两个用途:
调用父类的构造器:当使用super()时,它必须是子类构造器中的第一条语句。这样做是为了确保在子类对象实例化过程中,父类的构造器首先被调用,以正确初始化继承自父类的属性。如果子类的构造器没有显式调用super(),编译器将自动插入一个无参的super()调用,前提是父类有一个无参构造器。
访问父类的成员(属性和方法):在子类的任何方法(包括构造器)内,可以使用super.属性或super.方法名()的形式访问父类的属性和方法。这在以下几种情况下特别有用:
当子类需要覆盖(Override)父类的方法,但在新方法中仍然想要调用父类中被覆盖的方法。 当子类的字段隐藏(Hide)了父类的同名字段,但需要访问父类中被隐藏的字段。 因此,super()用于构造器中调用父类的构造器,且必须是第一行代码;而super.属性或super.方法()可用于子类的任何其他方法中调用父类的成员,不受位置限制。
为什么需要super 访问父类的方法和属性?
类中 方法调用的逻辑
属性的访问逻辑:
本质:继承的本质==>向上查找,以及通过super.[属性/方法] 访问直接访问父类
方法的重写/覆盖override
注意:
多态polymorphic 方法和对象具有多种形态,是面向对象的第三大特征,多态是建立在封装和继承基础之上的。 why? 当类越来越多时,传统方法,代码复用性不高,且不利于维护
2.对象的多态
向上转型: 本质:父类的引用指向了子类的对象 Animal animal = new Dog(); //animal 编译类型就是Animal,运行类型就是Dog animal.cry(); //运行时,执行的是子类的cry(),因为animal的运行类型就是Dog,所以cry就是Dog的cry
向上转型的规则
多态的向下转型
属性重写问题
第一个例子向上转型后,base.count 时,此时的编译类型是Base 父类,所以base.count直接从父类查找,即10 第二个例子,不是向上转型,sub.count 遵循属性查找规则,先找本类,有,即20
练习:
注意最后的b.display(); 方法运行看运行类型,此时b的运行类型是s,所以执行s的方法,输出的是20
object类详解
equal和 == 的区别 区别:
Hashcode
toString
重写:
finalize
类变量(静态变量) 最大的特点就是可以被类的所有实例共享。 所有的对象都共享的空间(在堆中单独的空间),都拥有的对象
访问: 类名.类变量名(推荐) 或者:对象名.类变量名
类方法(静态方法)
类的方法,可被所有的成员对象共享、调用;
类方法的经典使用场景:
main方法
代码块
补充细节:静态代码块与普通代码块
当同时有静态属性初始化和静态代码块时:
注意:构造器是最后执行的
当涉及继承时: 构造器内默认隐藏的两步:
综合案例
1.先加载类 1.1 加载父类的静态属性以及代码块 1.2 再加载子类静态属性以及代码块 2.再加载对象 2.1 执行子类的构造器super() 2.2 在执行子类构造器之前,隐藏了父类的super(),以及父类的普通代码块和普通属性的初始化操作,的执行 2.3 执行完2.2,相当于执行完了子类super之前的隐藏的东西,现在 执行子类的普通代码块以及普通属性的初始化(看代码顺序了) 2.4 再执行完子类构造器中的剩余内容
练习题:
注意:z在类调用时,完成静态属性和静态 代码块的加载;同时,静态代码块只执行一次;
单例设计模式
final -- 常量 当不希望类被继承、或者方法被重写时
赋值的地方有三个:
如果final修饰的属性是静态的,则初始化的位置只能是: 1.在定义时 2.在静态代码块中
不能在构造器中(因为此时static加载是在类时,而构造器则是在实例化的时候)
当只想要某个值,但是不需要类加载时,可以同时使用final和static
抽象类 当父类有方法,但是暂时不确定时。
抽象方法,就是没有实现的方法,即没有方法体的方法
public abstract void eat()
细节:
接口 在接口中抽象方法可以省略abstract
interface中可以写属性以及方法,方法只能有以下三种:
内部类 属性、方法、构造器、代码块、内部类 嵌套在一个类中的类,被称为内部类。
内部类分为四类:
1.局部内部类定义在方法中/代码块中 2.作用域在方法体里或代码中【也就是说内部类,只能在方法体中使用】 3.本质仍然是一个类 4.可以访问外部类的所有成员,包括私有的
匿名内部类
WHY? 什么时候会需要以及产生内部类? ==> 匿名内部类是用来简化开发的,匿名其实是系统自动分配一个用户看不到的名字的内部类,用来实例化
使用场景:基于接口的内部类 1.当我们想实现一个接口的方法,但不想创建一个新的类时,
IA tiger =new IA(){ // 注意:这里的编译类型是IA 接口, 运行类似就是**匿名内部类**,
此时是编译器自动执行的。
new ==>jdk底层在创建匿名内部类的时候,也同时创建了实例,并且把实例返回给了tiger
@Overwride
//实现接口的方法
}
2.基于类的匿名内部类 即原来有一个类,如果要继承之后重写,需要新建一个类,现在可以直接使用匿名的内部类来实现 如何写?
原来new一个对象,只需要 Father father = new Father(); 就可以,如何使用匿名内部类,则需要: Father father = new Father(){}; // 注意,多了对象的{},如果是抽象的父类,就需要重写抽象方法了
匿名内部类的最佳实践: 当作实参直接传递 代码简洁高效
3.成员内部类
放在成员位置
如何使用成员内部类的方法呢?
public class Homework07 {
public static void main(String[] args) {
Car car1 = new Car(60);
car1.getAir().flow();
}
}
class Car{
private double temperature;
public Car(double temperature) {
this.temperature = temperature;
}
class Air{
public void flow(){
if (temperature < 0){
System.out.println("吹暖风");
} else if (temperature > 40) {
System.out.println("吹冷风");
} else if (temperature > 0 && temperature < 40){
System.out.println("空调关闭");
}
}
}
// 成员内部类如何调用方法
public Air getAir(){
return new Air();
}
}
4.静态内部类
小结:
练习题:
Java枚举类 why?
枚举的实现方式: 1.自定义实现 2.使用 enum 关键字实现枚举
如何自定义实现枚举类? 1.将构造器私有化,这样就保证了无法在外部直接new的方式创建对象 2.去掉setXxx方法,防止属性被修改 3.在Season内部,直接创建固定的对象 4.优化,可以加入final修饰符,类不用加载了
使用关键字enum 来创建:
一些细节: 这里,如果实例化调用无参构造器时,可以只写常量名,把后面的()也省略掉; 比如下面:
例题:
这里w为什么是True? 因为Gender2.BOY 是一个静态对象,所以boy和boy2是相同的一个,所以是True
enum类的常用方法
可以实现j接口 不能继承其它类了,因为本身隐式继承了Enum类了(java单继承机制,即只能有一个直接的父类)
补充知识点:增强for循环
注解 Jdk的基本注解内容:
相当于做了一个语法的校验,加强检查的功能了。
deprecated 用于提醒,版本升级时的过渡使用
suppresswaring 抑制警告
练习题 1. 静态变量:只执行一次,所有在第二次创建对象初始化的时候,静态变量还保留着第一次初始化后修改的属性,所以颜色也是red
异常 Exception 意义在于让程序继续执行,而不是崩溃。
错误 Error -- java虚拟机无法解决的严重问题
异常又分为:编译时异常和运行时异常
编译异常
异常 Exception 意义在于让程序继续执行,而不是崩溃。
错误 Error -- java虚拟机无法解决的严重问题
异常又分为:编译时异常和运行时异常
编译异常
新建数组,但是是空数组,所以一开始就是空指针异常,到return 3 然后执行finally return 4; 最终的结果就是return 4
注意这里最后的返回值,与打印值。 finally没有返回值,只有打印值。
总结:异常分编译异常和运行异常,而编译异常必须处理(try catch 或者throw);而运行异常默认处理,不需要显式处理。
常见的编译异常和运行异常 编译异常:FileNOTFound、ClassNotFound 运行异常:除零异常/空指针异常
自定义异常 throw
方法的重载 overload
定义:一个类中方法名字相同,但是参数列表不同的方法。
方法的传参
可变参数
Java中允许同一个类中多个同名 同功能但是参数个数不同的方法,封装成一个方法。
==>对方法重载的一种优化,不用因为参数个数不同而写多个方法了
可以把传入的参数nums 视为数组