数组
定义语法
- int[] hens ={1,2,3,4,5},int[] arr = new int[] {1,2,3,4,5}(静态初始化)
- int[] hens =new int[5];(这里是定义一个有5个位置的int类型数组)
- 先声明数组int[] hens; 再创建数组hens = new int[5];
遍历数组
数组长度
数组名.length
一维数组使用细节
数组创建后如果没有负值,那么会有默认值
增强for循环(补充)
语法 for(数据类型 变量名 : 数组名){}
注意:数组和变量要一个类型
例子(用在枚举上): for(Season season : values){}
运行模式:从values数组依次取一个Season类型的对象,把地址传递给season
方法(成员方法)
成员方法的定义:
成员方法细节
- 如果想接收多个返回值?返回数组。
- 方法不可以嵌套定义。
- 同一个类的方法可以直接调用 直接上例子: Class A { public void print(int n) { System.out.println("print()方法被调 用 n=" + n); } public void sayOK() { print(10);//在这里方法print就被调 用了 } }
- 跨类调用方法:需要通过对象名调用 public void m1(){//这个方法是A类中的 B b = new B(); b.hi();//这个hi是B类中的 }
- 在成员方法中引用到了main方法中的数组或某个对象 然后再public void test200(Person p){ p = null;行参中的Person p是main方法中的一个对象,但是test200方法中的p与其不同,置空的只是test200中的p(也就是引用置空,实际引用地址是没有变化的)(这里也可以看出null这个代码并不是清空地址,只是取消引用),所以main方法中的p不发生任何变化。 }
方法递归调用
自己调 用自己:在tool方法里调用tool方法(有限套娃)(一定要有递归方法的出口,不然就是无限套娃了)
java
public void test(int n){
if (n > 2){//出口
test(n - 1);
}
System.out.println("n=" + n);
}//输出什么?2.3.4。
阶乘:
java
public int factorial(int n){//在前面传入参量 n = 5
if (n == 1){
return 1;
}else{
return factorial(n - 1) * n;
}
}//阶乘实际计算为1*2*3*4*5每次递归调用都要缩小范围,目标才能达到出口的条件。
方法重载
同一个方法名,有不同的形参
例如System.out.println();这个方法如果方法名不相同,那么就不叫重载。
返回值
return n1 > n2 ? n1 : n2;(这里的意思直接省略了if语句,而是用缩写公式完成了n1与n2的对比),这个语句也可以用在int a = n1 > n2 ? n1 : n2;
方法重载细节
public double max(double n1,double n2,double n3),在这个语句里如果前文给的形参是(double1,double2,int3)那么也是会匹配到上面这个方法,因为int类型可以转换为double。
但是max(double n1,double n2,double n3)与max(double n1,double n2,int n3)是方法重载
可变参数
java允许将同一个类中多个重名同功能但参数个数不同的方法,封装成一个方法。
可以通过可变参数实现。
基本语法:访问修饰符 返回类型 方法名(数据类型...形参名)//这里多个一个数据类型
public int sum(int... nums){}
还可以通过代码查询上面方法接受了几个参数 -> nums.length;
可变参数细节
-
可变参数可以和普通形参一起放在形参列表,但必须保证可变参数在最后
-
一个形参列表只能出现一个可变参数
-
可变参数进方法后是以类似数组的方式存在,读取其中数据也是用类似数组的方式读取
变量作用域(VarScope)
-
主要的变量是属性(成员变量)和局部变量。
-
局部变量一般指在成员方法中定义的变量
-
java中作用域的分类
- 全局变量:作用域为整个类体
- 局部变量:也就是除了属性之外的其他变量,作用域为定义它的代码块中!
-
全局变量可以不赋值,直接使用,因为有默认值。
局部变量必须赋值后,才能使用,因为没有默认值。
-
全局变量可以被本类使用,或其他类使用(通过对象调用):
javapublic void test() { Person p1 = new Person(); System.out.println(p1.name);//jack }
javapublic void test2(Person p) { System.out.println(p.name);//jack }
但局部变量只能在本方法中使用
-
全局和局部变量可以重名,访问时遵循就近原则(比如在方法中定义了一个name,又在类中定义了一个name,此时在方法中使用到了name,那么这个name属于局部变量)
-
属性可以加修饰符,局部变量不能加修饰符
构造器
在先前创建人类的对象时,是先把一个对象创建好后,再给他的年龄和姓名赋值。
现在要求在创建人类对象是,就直接指定这个对象的年龄和姓名,该怎么做?这时就使用构造器。
基本语法
[修饰符] 方法名 (形参列表){ 方法体; }
老韩说明
-
构造器的修饰符可以默认
-
构造器没有返回值
-
方法名和类名字必须一样
-
参数列表和成员方法一样的规则
-
构造器的调用系统完成
-
在创建对象时,系统会自动的调用该类的构造器完成对对象的初始化
构造器细节:
-
构造器也可以重载,使用方法与方法重载一致
-
构造器是完成对象的初始化,不是创建对象 例子如下
Person p1 = new Person("king", 40); //("king", 40)就是初始化内容
-
构造器要让系统自动调用,我们是无法调用的
-
一旦定义了自己的构造器,默认的构造器就覆盖了,就不能再使用默认的无参构造器,除非显式的定义一下,
即:Dog(){}(这点很重要)
构造器的复用
java
public Employee(String job, double sal){
this.job = job;
this.sal = sal;
}
public Employee(String name, char gender, int age){
this.name = name;
this.gender = gender;
this.age = age; }
public Employee(String job, double sal,String name, char gender, int age){
this(name, gender, age);//使用到前面的构造器 this. job = job; this.sal = sal;
}
对象创建流程分析
看一个案例
java
class Person{//类Person
int age = 90;
String name;
Person(String n; int a){//构造器
name = n;//给属性赋值
age = a;//...
}
main(){
Person p = new Person("小倩",20)
}
流程分析:
- 先在方法区加载Person类
- 然后在堆内开空间,创建含age和 name变量的地址1(对象)并完成默认初 始化age = 0,name = null
- 然后再完成对age的赋值(90)
- 接着执行构造器 a.在常量池创建一个地址2并接收"小倩" b.让地址1中的name指向地址2 c.再把age重新赋值为20
- 最后才在栈中创建一个p指向地址1
反编译
javap
this关键字------对构造器形参名的优化
this.变量名;//访问this所在成员属性中的成员变量 ThisDetail
-
访问成员方法的语法:this.方法名(参数 列表);
-
访问构造器语法:this(参数列表); 注意只能在构造器中用且是第一条语句 (即只能在构造器中访问另一个)
-
this不能在类定义的外部使用,只能在 类定义的方法中使用
同类不同方法相互调用
A. f1(); B. this.f1();
以上两种方法目前来说都能调用f1,但是具体区别在学到继承的时候
包
包的作用
-
区分相同名字的类
-
当类很多时可以很好的管理【API文档】
-
控制访问范围
包的基本语法
java
package com.hspedu
package 关键字,表示打包
com.hspedu 表示包名
包的本质分析
就类似文件夹
命名规则
只能包含数字.字母.下划线.小圆点
但不能数字开头或者关键字、保留字
命名规范
一般是小写字母+小圆点
一般是com.公司名.项目名.业务模块名
常用的包
java.lang.* //基本包,默认引入
java.util.* //工具包,工具类(Scanner)
java.net.* //网络包,网络开发
java.awt.* //是做界面开发,GUI
IDEA
文件储存位置说明
-
class文件编译后会储存在\out文件夹内
-
.java文件在scr文件夹内
常用快捷键
-
删除当前行,默认ctrl + Y 自己配置ctrl+d
-
复制当前行粘贴到下一行,Ctrl+alt+向下光标
-
补全代码alt+/
-
导入当前行需要的类alt+enter
-
快速格式化代码ctrl+alt+L
-
运行程序 shift + F10(如果想快捷运行, 要先配置主类)
-
生成构造器alt + insert
-
查看一个类的层级关系 F4
-
定位方法源 ctrl+鼠标左键
-
自动分配变量名,在new Scanner()后面 加.var,然后回车
模板快捷键
-
sout
-
fori
-
...自己找模板 file->settings->editor->Live templates
IDEA查看jdk源码
老韩文件夹里有doc文档可看
访问修饰符
- Public: java语言中访问限制最宽的修饰符,一般称之为"公共的 ",被其修饰的类,属性及方法,不仅可以跨类访问,而且允许跨包(package)访问
- Protected: 介于public和private之间的一种访修饰符,一般称之为"保护型 ",被其修饰的类,属性以及方法只能被该类本身的方法及子类访问,即使子类在不同的包中也可以访问.
- **Default: **即不加任何访问修饰符,通常称为"默认访问模式",即"默认型 ",该模式下,只允许在同一个包中进行访问.
- Private: java语言中访问权限限制最窄的修饰符,一般称之为"私有的 ",被其修饰的类,属性,以及方法只能被该类的对象访问,其子类不能访问,更不允许跨包访问.
访问修饰符图片助解
封装(encapsulation)
封装的理解和好处
理解
封装就是把抽象出的数据(属性)和对数据的操作(方法)封装在一起,数据被保护在内部,程序的其他部分只有通过被授权的操作(方法),才能对数据进行操作
好处
-
隐藏实现细节
-
可以对数据进行验证,保证安全合理
封装的实现步骤
-
将属性进行私有化private【不能直接修改属性】
-
提供一个公共的set方法,用于对属性判断并赋值
javapublic void setXxx(类型 参数名){ //加入数据验证的业务逻辑 属性 = 参数名; }
-
提供一个公共的(public)get方法,用于获取属性的值
javapublic 数据类型 getXxx(){ //权限判断,Xxx某个属性 return Xxx; }
将构造器与封装结合
将set方法写在构造器内
继承
为什么要使用继承?
当多个类的属性和方法大量重复(需要提高代码复用性)
继承的基本语法
class 子类 extends 父类{ }(子类又叫派生类,父类叫基类,超类)
继承的细节问题
a. 子类不能直接引用父类的私有属性,要通过父类 公共的方法去访问
b. 子类必须调用父类的构造器,完成父类的初始化
c. 创建子类对象时,不管使用子类的那个构造器,默认情况下总会去调用父类的无参,如果父类没有无参,则必须在子类的构造器中用 super去指定使用父类的某个构造器完成对父类的初始化工作 //super();
d. 如果希望指定去调用父类的某个构造器,则显示的调用一下super(参数列表)
e. super必须放在构造器第一行
f. super() 和 this()都只能放在构造器第一行,因此他俩不能共存在一个构造器
g. 不能滥用继承,子类和父类之间必须满足:子类 is a 父类
继承的内存布局
java
public class ExtendsTheory {
public static void main(String[] args) {
Son son = new Son();
System.out.println(son.name);
System.out.println(son.hobby);
}
}
class GrandPa { //爷类
String name = "大头爷爷";
String hobby = "旅游";
}
class Father extends GrandPa {//父类
String name = "大头爸爸";
private int age = 39;
public int getAge() {
return age;
}
}
class Son extends Father{ //子类
String name = "大头儿子";
}
//当Son son = new Son();这条语句执行时 会先在方法区内加载各个类,并且父类优先然后再堆中开空间从父类优先一个个创建属性并赋值(值来自方法区)
访问属性的原理
优先看子类中有没有需要的属性,如果有并且能访问,则返回信息;如果没有,就向上(父类)查找
super关键字
A. 访问父类的属性,但不能访问父类的private属性//super.属性名
B. 访问父类的方法,也是不能访问private //super.方法名(参数列表)
C. 访问父类的构造器,只能放在构造器的第一句,只能出现一句!只能在构造器!//super(参数列表)
一般只有子类中定义了与父类重名的属性、方法才会使用到super,如果上一个父类没有super指定的,则继续找父类的父类
super和this的冲突
在构造器中两者都只能写在第一行,所以构造器最多只能有他俩其一
方法重写
是什么?
简单来说子类中的一个方法与父类的某个方法的名称、返回类型、参数都一样,那么这个方法就称为覆盖(重写)了父类的方法
细节
a.返回类型可以不一样,但父类中的返回值得是子类中的返回值的父类,如父类是Object子类是String
b.子类方法不能缩小父类方法的访问权限
多态
为什么要使用多态?
譬如一个项目,有主人有动物,有食物又因为食物很多,喂食所需要的方法就 很多,不利于管理和维护(方法复用性不高,不利于管理与维护)
多态的具体体现
a. 方法的多态(方法重载/重写)
b. 对象的多态
重要的几句话
(1) 一个对象的编译类型和运行类型可以不一致比如Animal animal = new Dog();//Dog是Animal的子类
(2) 编译类型在定义对象时,就确定了,不能改变
(3) 运行类型是可以变化的
(4) 编译类型看定义时 = 号的左边,运行类型看 =号的右边
(5) 编译类型与运行类型的区别
当Dog是Animal的子类时
java
Animal animal = new Dog();
animal.cry();//这个时候cry运行的是Dog子类中的cry,因为运行时执行到该行时animal的运行类型是Dog,所以cry是Dog的cry
animal = new Cat();//这个时候运行类型又成了Cat
(6)把每个方法的形参都改为具体参数的父类,方法的复用性就极大地提高了
多态细节
a. 多态的前提:两个对象存在继承关系
b. 多态的向上转型
本质:父类的引用指向了子类的对象
语法:父类类型 引用名 = new 子类类型();
特点:编译类型看左边,运行类型看右边。
可以调用父类中的所有成员(遵守访问权限),但不能调用子类中特有成员;
//比如
Animal animal = new Cat();
animal.catchMouse();//这里会报错,无法调用Cat类的catchMouse方法
最终运行效果看子类的具体体现
c. 多态的向下转型
语法:子类类型 引用名 = (子类类型) 父类引用
Cat cat = (Cat) animal
只能强转父类的引用,不能强转父类的对象
要求父类的引用必须指向的是当前目标类型的对象
可以调用子类类型中的所有成员就像是把向上转型给直接换成Cat cat = new cat();
属性重写问题
属性没有重写之说!属性的值看编译类型
instanceOf比较操作符
用于判断对象的运行类型是否为XX类型或XX类型的子类型
语法:aa instanceOf AA;//返回值为boolean
Sub s = new Sub();
Base b = s;//Base是Sub的父类
此时 b == s的返回值是true
java动态绑定机制(非常非常重要)
a.当调用对象方法的时候,该方法会和该对象的内存地址/运行属性绑定
b.当调用对象属性时,没有动态绑定机制,哪里声明,哪里使用
多态的应用(多态数组)
创建了包含多个对象的数组,可以通过循环遍历
特殊情况:
Person student = new Student();
//这里Student是Person的子类
//Student中有一个study成员方法
//但Person中没有这个方法,
//但是可以考虑用if配合instanceof来选择是否
equals方法
==和equals比对象比的是运行类型,(也就是判断引用地址是否相同)
区别:当equals比String类型时如果地址不相同,他会继续判断字符串长度是否相同,如果相同,会继续判断每个字符是否一一对应
equals只能判断引用类型
直接上代码:if(name.equals("丁真")){......}
重写equals方法
为什么要重写?
Object里面最原始的equals只是比较两个对象是否一样(如person1 != person2),不一样就直接返回false,但是有时候不同对象属性一样,就需要重写新的equals
新需求:equals比较的是两个有可能不是同一个的对象的属性是否一样,一样就返回true。
组织一下语言:
== :比较左右两个东西是不是一个东西
equals(Object):比较左右两个对象是不是一个对象
equals(重写):先比较是不是一个类型,再比较内容
hashCode
老韩的6个小结:
1)提高具有哈希结构的容器的效率
2)两个引用,如果指向的是同一个对象,则哈希值肯 定是一样的
3)两个引用,如果指向的是不同对象,则哈希值是不一样的
4)哈希值主要根据地址号来的,不能完全将哈希值等价于地址.
5)案例演示[HashCode_.java]:obj.hashuCode()
A aa = new A();
A aa2 = new A();
aa3 = aa;
6)后面在集合中hashCode如果需要的话,也会重写
toString方法
默认返回: 全类名 + @ + 哈希值的十六进制
子类往往重写toString方法,用于返回对象的属性信息
调用语法:
1.对象名.toString();
2.对象名;//真就这么简单
重写toString方法
使用快捷键即可alt + insert(IDEA)
finalize
当垃圾回收器确定不存在对该对象的更多引用时,由对象的垃圾回收器调用此方法
解释:
-
当对象被回收时,系统自动调用该对象的finalize方法,子类可以重写该方法,做一些释放资源的操作。
-
什么时候被回收:当某个对象没有任何引用时,则jvm就认为这个对象是一个垃圾对象,就会使用垃圾回收机制来销毁该对象,在销毁该对象前,会先调用finalize方法
-
垃圾回收机制的调用,是由系统来决定(GC回收算法),也可以通过System.gc()主动触发垃圾回收机制(不一定能成功触发)
-
Object类的finalize方法是空的,什么都没干
ps:实际开发中一般不会用...应付面试官
零钱通
化繁为简:
-
先完成显示菜单,并可以选择菜单,给出相应提示
-
完成零钱通明细
老韩思路:
- 可以把收益入账和消费,保存到数组;
- 可以使用对象
- 简单的话可以使用String拼接
System.out.println("1 零钱通明细"); System.out.println("------------------------------------零钱通明细------------------------------------");
-
完成收益入账 完成功能驱动程序员增加新的变量和代码
老韩思路:定义新的变量
javadouble money = 0; double balance;
//需要获取系统时间^1^
-
完成消费信息
定义新变量place//消费地点
-
完成退出提示
至此,以上是面向过程的方法完成零钱通的流程。
Chapter08Homeworks
断点调试(IEDA)
当程序运行到断点处时,程序暂停,程序员可以开始一步步往下运行
快捷键:
F7:跳入方法内
F8:逐行执行代码
shift + F8:跳出方法
F9:执行到下一个断点//可以在dubug过程中动态的下断点
进入Jdk源码有问题?
解决方法1:
使用force step into:快捷键 alt + shift + F7
解决方法2:
配置一下(方法在330集)
chapter09
先停在第342集,转枚举
枚举
枚举(enumeration)的引出
现在有个需求:创建季节(Season)对象,请设计并完成
java
//思路清晰,先建个类,给属性1.name 2.desc(描述)
class Season{
private String name;
private String desc;
//构造器
//_get()
}
public class Main{
public static void main(String args[]){
Season summer = new Season("夏天","炎热");
Season spring = new Season("春天","温暖");
Season autumn = new Season("秋天","凉爽");
Season winter = new Season("冬天","寒冷");
Season other = new Season("红天","666");
}
}
出现问题:因为对于季节而已,他的对象(具体值),是固定的四个,不会有更多
按上面的设计思路,不能体现季节是固定的四个对象
甚至可以通过set方法将summer.name改为别的字符串
因此,这样的设计不好......==>枚举的需求诞生【枚:一个一个 举:列举;即把具体的对象一个一个列举出来】
自定义枚举类
java
class Season{
private String name;
private String desc;
//1.将构造器私有化,目的防止直接new
//2.去掉setXxx的相关方法,只能读,不能修改
//3.在Season内部,直接创建固定的对象
public static final Season SPRING = new Season("春天","温暖");
public static final Season SUMMER = new Season("夏天","炎热");
public static final Season AUTUMN = new Season("秋天","凉爽");
public static final Season WIMTER = new Season("冬天","寒冷");
private Season(String name, String desc) {
super();
this.name = name;
this.desc = desc;
}
public String getName() {
return name;
}
public String getDesc() {
return desc;
}
@Override
public String toString() {
return "Season [name=" + name + ", desc=" + desc + "]";
}
}
public class Enumeration02 {
public static void main(String args[]) {
System.out.println(Season.SPRING);
}
}
写完发现,代码很长,很费事,那么就需要引出:
使用enum关键字实现枚举类
- 将定义常量对象放在最前面
- 使用关键字 enum 替代 class
- public static final Season SPRING = new Season("春天","温暖") 直接使用SPRING("春天","温暖")来替代
- 如果有多个对象,使用","间隔 :SPRING("春天","温暖"),SUMMER("夏天","炎热"),......;
enum关键字实现枚举注意事项
-
当我们使用 enum 关键字开发一个枚举类时,默认会继承 Enum 类[如何证],老师使用 javap 工具来演示
-
传统的 public static final Season2 SPRING = new Season2("春天","温暖");简化成 SPRING ("春天","温暖"),这里必须知道,它调用的是哪个构造器.
-
如果使用无参构造器创建枚举对象,则实参列表和小括号都可以省略
-
当有多个枚举对象时,使用,间隔,最后有一个分号结尾
-
枚举对象必须放在枚举类的行首
java
enum Gender2{ /父类 Enum 的 toString
BOY,GIRL;
}
Gender2 boy = Gender2.BOY://oK
Gender2 boy2 = Gender2.BOY;//枚举类的对象是可以传递的
- 父类Enum的toString方法返回的是枚举对象的对象名,比如上面的代码,boy.toString()就是返回BOY
enum常用方法说明
valueOf 方法说明
enum细节
-
使用enum关键字后,就不能再继承其他类了,因为enum会隐式继承Enum,而Java是单继承机制
-
枚举类和普通类一样,可以实现接口,如下形式
enum 类名 implements 接口1,接口2{}
注解( Annotation )
注解的理解
- 注解( Annotation )也被称为元数据( Metadata ),类、方法、属性、构造器、局部变量等数据信息。
- 和注释一样,注解不影响程序逻辑,但注解可以被编译或运行,相当于嵌入在代码中的补充信息。
- 在 JavaSE 中,注解的使用目的比较简单,例如标记过时的功能,忽略警告等。在 JavaEE 中注解占据了更重要的角色,例如用来配置应用程序的任何切面,代替 java EE 旧版中所遗留的繁冗代码和 XML 配置等。
基本的Annotation的介绍
^: SimpleDateFormat formatter = new SimpleDateFormat("dd-MM-yyyy HH:mm:ss");