第一阶段:
Hello.java ---- javac 编译 ---> Hello.class ---- javap 反编译------> Hello.java
import java.util.Scanner;//表示把java.util下的Scanner类导入
public class Example{
public static void main(String[] args){
String str = "123.0";//字符串的形式得能转成double型
double d = Double.parseDouble(str);
System.out.println(d);
char c = '赵';
String a = c + "";
System.out.println(a);
}
}
类就是数据类型,比如说Cat
对象就是一个具体的实例
java 数据类型分为 :基本数据类型和引用数据类型
基本数据类型有8种:数值型(整数类型,byte、short、int、long,浮点类型,float、double)、字符型(char)、布尔型(boolean)
引用数据类型有: 类、接口、数组 引用数据类型是地址拷贝 类属于一个数据类型
访问修饰符(作用是控制变量的范围) 有四种:public protected 默认 private
引用类型指向堆空间
执行一个方法时,就创建一个新的受保护的独立空间(栈空间) 成员(属性 + 方法)
字段、属性、成员变量 概念是一个意思 访问属性:对象名.属性名
在属性定义的变量是全局变量(在自己的类里面的方法可以直接使用全局变量),有默认值可以直接使用(不管程序有没有显示的初始化,Java 虚拟机都会先自动给它初始化为默认值),规则跟数组一致 ,可以加修饰符
在方法定义的变量是局部变量(除了属性之外的其他变量,作用域为定义它的代码块中) ,没有默认值,不能直接使用,不能加修饰符
方法重载对返回类型没有要求
构造器是对新对象属性的初始化(方法名与类名相同,没有返回值,不用写void,由系统自动调用)
构造器的修饰符可以默认, 也可以是 public protected private
如果没有定义构造器,系统会自动给类生成一个默认无参构造器(默认构造器)
一旦定义了自己的构造器,默认的构造器就覆盖了,就不能再使用默认的无参构造器,除非自己再显式的定义一下
对象名是对对象的引用,不是对象
this 不能在类定义的外部使用,只能在类定义的方法中使用,this关键字可以用来访问本类的属性、方法、构造器
this访问构造器语法:this(参数列表) 注意只能在构造器中使用(即只能在构造器中访问另外一个构造器,必须放在第一条语句)
idea 快捷键 ctrl + d (删除当前行) ctrl + alt + 向下光标(复制当前行) alt + / (补全代码) ctrl + / (添加注释和取消注释) alt + enter(导入该行需要的类) ctrl + alt + L (快速格式化代码) alt + R (快速运行程序) alt + insert(生成所需要,如构造器) ctrl + H(查看一个类的层级关系) ctrl + B(将光标放到方法上,定位到方法) 加.var(自动分配变量名)、ctrl + z 撤回(有的需要自己修改)
包的本质: 实际上就是创建不同的文件夹来保存类文件
包的命名 com.公司名.项目名.业务模块名
建包防止类名冲突,进行区分
java 常用包 : java.lang //lang是基本包,默认引入,不需要再引入
java.util //util包 ,系统提供的工具包,工具类,使用Scanner
java.net //网络包,网络开发
java.awt //是做Java的界面开发,GUI
包的注意事项和使用细节:
1. package 的作用是声明当前类所在的包,需要放在类的最上面,一个类中最多只有一句package
2.import 指令 位置放在package的下面,在类定义前面,可以有多句且没有顺序要求
public > protected > 默认 > private
访问级别 访问控制修饰符 同类 同包 子类 不同包
公开 public y y y y
受保护 protected y y y f
默认 没有修饰符 y y f f
私有 private y f f f
oop编程的三大特征:封装、继承、多态
封装的实现步骤(三步)
封装(encapsulation)就是把抽象出的数据[属性]和对数据的操作[方法]封装在一起,数据被保护在内部,程序的其它部分只有通过被授权的操作[方法],才能对数据进行操作。
1.将属性进行私有化private【不能直接修改属性】
2.提供一个公共的(public)set方法,用于对属性判断并赋值
public void setXxx(类型 参数名){
//加入数据验证的业务逻辑
属性 = 参数名;
}
3.提供一个公共的(public)get方法,用于获取属性的值
public XX getXxx(){
return xx;
}
继承:子类继承了所有的属性和方法,非私有的属性和方法可以在子类直接访问
但是私有属性和方法不能在子类直接访问,要通过父类提供公共的方法去访问
子类必须调用父类的构造器,完成父类的初始化
当创建子类对象时,不管使用子类的哪个构造器,默认情况下总会去调用父类的无参构造器,如果父类没有提供无参构造器,则必须在子类的构造器中去用super去指定父类的哪个构造器完成对父类的初始化工作,否则,编译不会通过
java所有类都是Object类的子类,Object是所有类的基类
继承案例:
class A{
A(){System.out.println(“a”);}
A(String name){System.out.println(“a name”);}
}
class B extends A{
B(){this(“abc”);System.out.println(“b”);}//有this了,不能再加super()了
//this关键字可以用来访问本类的属性、方法、构造器
B(String name){super();System.out.println(“b name”);}//自动加上一个super()
}
main 中 B b = new B();
输出: a b name b
继承设计的基本思想: 父类的构造器完成父类属性初始化,子类构造器完成子类属性初始化
super可以访问父类的属性和方法,但不能访问private
super(参数列表);(访问父类构造器,只能放在构造器的第一句,只能出现一句)
找xx方法是(xx( ) 和 this.xx( ) 一样) , 顺序是:
- 先找本类,如果有,则调用
- 如果没有,则找父类(如果有,并可以调用,则调用)
- 如果父类没有,则继续找父类的父亲,整个规则,就是一样的,直到Object类
提示: 如果查找方法的过程中,找到了,但是不能访问,则报错,cannot access
如果查找方法的过程中,没有找到,则提示方法不存在
super super.xx( )查找xx方法的顺序是直接查找父类,其他规则一样
属性和方法一样
重写(覆盖):子类的形参列表、方法名称要与父类相同,子类的返回类型与父类相同或是父类的子类,子类的访问修饰符不能缩小父类的范围
多态具体体现
- 方法多态 (1)重载体现多态 (2)重写体现多态
- 对象多态
- 对象的编译类型和运行类型可以不一致,编译类型在定义时,就确定,不能改变
- 对象的运行类型是可以变化的,可以通过getClass()来查看运行类型
- 编译类型 = 号的左边,运行类型 = 号的右边
对象的多态:
- 一个对象的编译类型和运行类型可以不一致
- 编译类型在定义对象时,就确定了,不能改变
- 运行类型是可以变化的
- 编译类型看定义时 = 号的左边,运行类型看 = 号的右边
Animal animal = new Dog(); 【animal 编译类型是Animal,运行类型Dog】
//父类的引用指向子类的对象
animal = new Cat(); 【animal 的运行类型变成了Cat,编译类型仍然是Animal】
多态的前提是:两个对象(类)存在继承关系
多态:方法或对象具有多种形态,是OOP的第三大特征,是建立在封装和继承基础之上
多态的向上转型:
- 本质:父类的引用指向了子类对象
- 语法:父类类型 引用名 = new 子类类型();
- 特点:编译类型看左边,运行类型看右边
可以调用父类中的所有成员(需遵守访问权限) 成员 = 属性 + 方法
不能调用子类的特有成员(意思就是这个方法在父类和子类中都能找到,如果先能通过编译类型,就说明这个方法在父类中存在,运行时先在子类中找这个方法,若没有则在父类(编译类型)中一定存在此方法)
最终运行效果看子类的具体实现 //访问运行类型
多态的向下转型:
- 语法:子类类型 引用名 = (子类类型) 父类引用
- 只能强转父类的引用,不能强转父类的对象
- 要求父类的引用必须指向的是当前目标类型的对象
- 当向下转型后,可以调用子类类型中所有的成员
//因为在编译阶段,能调用哪些类型,是由编译类型决定的
//最终运行效果看子类的具体实现,即调用方法时,按照从子类开始查找方法
//把指向子类对象的父类引用,转成指向子类对象的子类引用
访问属性的值看编译类型
instanceOf 比较运算符,用于判断对象的运行类型是否为XX类型或XX类型的子类型
instanceof是Java的一个保留关键字,左边是对象,右边是类,返回类型是Boolean类型。它的具体作用是测试左边的对象是否是右边类或者该类的子类创建的实例对象,是,则返回true,否则返回false。
动态绑定机制:
- 当调用对象方法时,该方法会和对象的内存地址/运行类型绑定(继承的查找关系)
- 当调用对象属性时,没有动态绑定机制,哪里声明,哪里使用
Object类详解:
== 和 equals的对比
== 是一个比较运算符
- ==: 是一个比较运算符
- ==: 如果判断基本类型,判断的是值是否相等
- ==: 如果判断引用类型,判断的是地址是否相等,即判定是不是同一个对象
- euqals: 是Object类中的方法,只能判断引用类型
- 默认判断的是地址是否相等,子类中往往重写该方法,用于判断内容是否相等。比如
Integer,String
类的全类名:包名 + 类名
toString方法
默认返回: 全类名 + @ + 哈希值的十六进制
子类往往重写toString方法,一般用于返回对象的属性信息
当直接输出一个对象时,toString方法会被默认的调用
断点调试的快捷键:
F7(跳入方法内) F8(跳过,逐行执行代码) shift + F8(跳出方法) F9(resume,执行到下一个断点)
public void test(Person p){
if(p instanceof Student){
((Student) p).study();
}
else if(p instanceof Teacher){
((Teacher) p).teach();
}else{
System.out.println("do nothing...");
}
}
第二阶段
类.方法() =>因为当一个方法是static时,就是一个静态方法,静态方法可以直接通过类名调用
static变量是同一个类所有对象共享,在类加载的时候就生成了(new的时候加载一次)
类变量的访问: 类名.类变量名 或 对象名.类变量名(没有创建对象实例也可以访问)
当方法使用了static修饰后,该方法就是静态方法,静态方法可以访问静态属性/变量
当方法中不涉及任何和对象相关的成员,则可以将方法设计成静态方法,提高开发效率
比如:工具类中的方法utils
Math类、Arrays类、Collections集合类
类方法中不允许使用和对象有关的关键字,静态方法只能访问静态成员
非静态方法可以访问 静态成员和非静态成员
main方法是虚拟机调用,不必创建对象 该方法String类型的数组参数,该数组中保存执行java命令时传给所运行的类的参数
静态方法main要访问本类的非静态成员,需要先创建对象,再调用即可
代码块:
static代码块也叫静态代码块,作用就是对类进行初始化,而且它随着类的加载而执行,并且只会执行一次。如果普通代码块,每创建一个对象,就执行。普通代码块相当于构造器的补充
类什么时候被加载:
- 创建对象实例时(new)
- 创建子类对象实例,父类也会被加载
- 使用类的静态成员时(静态属性,静态方法)
创建一个对象时,在一个类调用顺序是: 先调用静态的,再调用普通的(一样的出现多个,按优先级来调用)
构造器的最前面其实隐含了super()和调用普通代码块
单例模式(饿汉式):
1.构造器私有化 => 防止直接new
2.类的内部创建对象
3.向外暴露一个静态的公共方法
4.代码实现
单例模式的两种实现方式(1)饿汉式(2)懒汉式 饿汉式的问题:在类加载时候就创建,可能存在资源浪费问题 懒汉式的问题:线程安全问题,后面学习了线程后,再进行完善
final 可以修饰类、属性、方法和局部变量
抽象类:
父类方法不确定性的问题,考虑将该方法设计为抽象(abstract)方法
所谓抽象方法就是没有实现的方法(没有方法体)
当一个类中存在抽象方法时,需要将该类声明为abstract类
细节:
抽象类不能被实例化,但抽象类的本质还是类
抽象类不一定要包含abstract方法,但有abstract方法一定是抽象类
abstract只能修饰类和方法,不能修饰属性和其它的
如果一个类继承了抽象类,则它必须实现抽象类的所有抽象方法,除非它自己也声明为abstract类
抽象方法不能使用private、final和static来修饰,因为这些关键字都是和重写相违背的。
接口就是给出一些没有实现的方法,封装到一起,到某个类要使用的时候,在根据具体情况把这些方法写出来
语法: interfance 接口名{
//属性
//方法(1.抽象方法2.默认实现方法3.静态方法)
}
class 类名 impletments 接口{
自己属性;
自己方法;
必须实现的接口的抽象方法
}
- 在jdk 7.0 前接口里的所有方法都没有方法体
- Jdk 8.0 后接口类可以有静态方法,默认方法,也就是说接口中可以有方法的具体实现
在jdk 8.0 后,可以有静态方法,可以有默认实现方法,需要使用default关键字修饰
在接口中,抽象方法,可以省略abstract关键字
如果一个类 implements实现接口 需要将该接口的所有抽象方法都实现
接口不能被实例化,接口中所有的方法是public方法,接口中抽象方法,可以不用abstract修饰
一个普通类实现接口,就必须将该接口的所有方法都实现
抽象类实现接口,可以不用实现接口的方法
一个类同时可以实现多个接口 class xx implements x,xxx
接口中的属性,只能是final的,而且是public static final 修饰符。比如: int a = 1;实际上是public static final int a = 1;(必须初始化)
接口中属性的访问形式:接口名.属性名
一个接口不能继承其它的类,但是可以继承多个别的接口
Inferface A extends B,C{}
接口的修饰符 只能是public 和 默认,这点和类的修饰符是一样的
接口(like - a) vs 继承类(is - a)
当子类继承了父类,就自动的拥有父类的功能
如果子类需要扩展功能,可以通过实现接口的方式来扩展
实现接口 是对java单继承机制的一种补充
接口在一定程度上实现代码解耦【即:接口规范性 + 动态绑定】
接口的多态特性
- 多态参数
在前面的Usb接口案例,Usb usb,既可以接收手机对象,又可以接受相机对象,就体现了接口多态(接口引用可以指向实现了接口的类的对象)
- 多态数组
案例:给Usb数组中,存放Phone和相机对象,Phone类还有一个特有的方法call(),请遍历Usb数组,如果是Phone对象,除了调用Usb接口定义的方法外,还需要调用Phone特有方法call.
- 接口存在多态传递现象
小结:类的五大成员(1)属性 (2)方法 (3)构造器 (4)代码块 (5)内部类
内部类(累不累)
一个类的内部又完整的嵌套了另一个类结构。被嵌套的类称为内部类,嵌套其他类的类成为外部类。内部类最大的特点就是可以直接访问私有属性,并且可以体现类与类之间的包含关系。
基本语法
class Outer{//外部类
class Inner{//内部类
}
}
class Other{//外部其他类
}
内部类的分类(4种)
定义在外部类局部位置上(比如说方法内):
- 局部内部类(有类名)
- 匿名内部类(没有类名)(重要!!!)
定义在外部类的成员位置上:
- 成员内部类(没用static修饰)
- 静态内部类(使用static修饰)
记住:
1.局部内部类定义在方法中/代码块
2.作用域在方法体或者代码块中
3.本质仍然是个类
匿名内部类的使用(本质是类、内部类、该类没有名字、同时还是一个对象)
说明:匿名内部类是定义在外部类的局部位置,比如说方法中,并且没有类名
创建匿名内部类时须继承一个已有的父类或实现一个接口,由于匿名类本身无名,因此也就不存在构造方法,而且匿名类不能重复使用
- 匿名内部类的基本语法
new 类或接口(参数列表){
类体
};
- 匿名内部类的语法比较奇特,因为匿名内部类既是一个类的定义,同时它本身也是一个对象,因此从语法上看,它既有定义类的特征,也有创建对象的特征,可以调用匿名内部类方法
- 可以直接访问外部类的所有成员,包含私有的
- 不能添加访问修饰符,因为它的地位就是一个局部变量
- 作用域: 仅仅在定义它的方法或代码块
- 匿名内部类---访问------>外部类成员
7.外部其他类---不能访问--->匿名内部类(因为匿名内部类地位是一个局部变量)
8.如果外部类和匿名内部类的成员重名时,匿名内部类访问的话,默认遵循就近原则,如果想访问外部类的成员,则可以使用(外部类名.this.成员) 去访问
Father father = new Father(“ jack ”){ //编译类型是Father ,运行类型是 xxx
//注意(“jack”)参数列表会传递给构造器
} //返回了xxx的对象
class xxx extends Father{
}
public class LocalInnerClass {
public static void main(String[] args) {
CellPhone cellPhone = new CellPhone();
cellPhone.alarmclock(new Bell() {
@Override
public void ring() {
System.out.println("lll");
}
});
}
}
interface Bell{
void ring();
}
class CellPhone{
public void alarmclock(Bell bell){
bell.ring();
}
}
成员内部类(成员内部类是定义在外部类的成员位置,并且没有static修饰)
- 可以直接访问外部类的所有成员,包含私有的
- 可以添加任意访问修饰符(public、protected、默认、private),因为它的地位就是一个成员
- 作用域和外部类的其他成员一样,为整个类体比如前面案例,在外部类的成员方法中创建成员内部类对象,再调用方法
- 成员内部类---访问---->外部类成员(比如:属性)[访问方式:直接访问]
- 外部类---访问----->内部类 访问方式:创建对象,再访问
- 外部其他类---访问---->成员内部类
- 如果外部类和内部类的成员重名时,内部类访问的话,默认遵循就近原则,如果想访问外部类的成员,则可以使用(外部类名.this.成员)去访问
静态内部类的使用(静态内部类时定义在外部类的成员位置,并且有static修饰)
- 可以直接访问外部类的所有静态成员,包含私有的,但不能直接访问非静态成员
- 可以添加任意访问修饰符(public、protected、默认、privated),因为它的地位就是一个成员
- 作用域:同其他的成员,为整个整体
- 静态内部类---访问--->外部类(比如:静态属性) [访问方式:直接访问所有静态成员]
- 外部类---访问---->静态内部类 访问方式:创建对象,再访问
如果不想创建一个外部对象,就想返回一个静态内部类的对象,那么你就要使用
public class StaticInnerClass01{
public static void main(String[] args) {
Outer10 outer10 = new Outer10();
outer10.m1();
//外部其他类 使用静态内部类
//方式 1
//因为静态内部类,是可以通过类名直接访问(前提是满足访问权限)
Outer10.Inner10 inner10 = new Outer10.Inner10();
inner10.say();
//方式 2
//编写一个方法,可以返回静态内部类的对象实例.
Outer10.Inner10 inner101 = outer10.getInner10();
System.out.println("============");
inner101.say();
Outer10.Inner10 inner10_ = Outer10.getInner10_();
System.out.println("************");
inner10_.say();
}
}
class Outer10 { //外部类
private int n1 = 10;
private static String name = "张三";
private static void cry() {}
//Inner10 就是静态内部类
//1. 放在外部类的成员位置
//2. 使用 static 修饰
//3. 可以直接访问外部类的所有静态成员,包含私有的,但不能直接访问非静态成员
//4. 可以添加任意访问修饰符(public、protected 、默认、private),因为它的地位就是一个成员
//5. 作用域 :同其他的成员,为整个类体
static class Inner10 {
private static String name = "韩顺平教育";
public void say() {
//如果外部类和静态内部类的成员重名时,静态内部类访问的时,
//默认遵循就近原则,如果想访问外部类的成员,则可以使用 (外部类名.成员)
System.out.println(name + " 外部类 name= " + Outer10.name);
cry();
}
}
public void m1() { //外部类---访问------>静态内部类 访问方式:创建对象,再访问
Inner10 inner10 = new Inner10();
inner10.say();
}
public Inner10 getInner10() {
return new Inner10();
}
public static Inner10 getInner10_() {
return new Inner10();
}
}
第三阶段
枚举实现的两种方式:自定义类实现枚举、使用enum关键字实现枚举
自定义:
- 将构造器私有化,目的防止 直接new
- 去掉setXxx方法,防止属性被修改
- 在类内部,直接创建固定的对象(对外暴露对象)
- 优化,可以加入final修饰符
enum关键字:
- 使用关键字enum替代class
- 常量名(实参列表)
- 如果有多个常量(对象),使用,号间隔即可
- 如果使用enum来实现枚举,要求将定义变量对象,写在前面 Color green = Color.GREEN
- 如果我们使用的是无参构造器,创建常量对象,则可以省略()
for(int i : nums){
}
使用enum关键字后,就不能再继承其它类了,因为enum会隐式继承Enum,而Java是单继承机制
枚举类和普通类一样,可以实现接口 enum类名implements接口1,接口2{}
修饰注解的注解成为元注解
@Deprecated:用于表示某个程序元素(类,方法等)已过时,可以修饰方法、类、字段、包、参数等等 可以做到新旧版本的兼容和过渡
@SuppressWarning中的属性介绍以及属性说明
1. 当我们不希望看到这些警告的时候,可以使用 SuppressWarnings 注解来抑制警告信息
2. 在{""} 中,可以写入你希望抑制(不显示)警告信息
3. 可以指定的警告类型有
JDK 的元 Annotation 用于修饰其他 Annotation
Retention //指定注解的作用范围,三种 SOURCE,CLASS,RUNTIME
Target // 指定注解可以在哪些地方使用
Documented //指定该注解是否会在 javadoc 体现
Inherited //子类会继承父类注解
RetentionPolicy.SOURCE: 编译器使用后,直接丢弃这种策略的注释
RetentionPolicy.CLASS: 编译器将把注解记录在 class 文件中. 当运行 Java 程序时, JVM 不会保留注解。 这是默认
值
RetentionPolicy.RUNTIME:编译器将把注解记录在 class 文件中. 当运行 Java 程序时, JVM 会保留注解. 程序可以通过反射获取该注解
异常(ArithmeticException 将程序执行中发生的不正常情况成为”异常” 开发过程中的语法错误和逻辑错误不是异常):
当一段代码出现异常/问题时,可以使用try-catch异常处理机制解决,从而保证代码健壮性
将代码-->选中-->快捷键 ctrl + alt + t --> 选中 try--catch
如果进行了异常处理,那么即使出现了异常,程序可以继续执行
1) NullPointerException 空指针异常
2) ArithmeticException 数学运算异常
3) ArrayIndexOutOfBoundsException 数组下标越界异常
4) ClassCastException 类型转换异常
5) NumberFormatException 数字格式不正确异常[]
编译异常是指在编译期间,就必须处理的异常,否则代码不能通过编译
try-catch-finally
程序员在代码块中捕获发生的异常,自动处理
throws
将发生的异常抛出,交给调用者(方法)来处理,最顶级的处理者就是JVM
可以进行try-finally 配合使用,这种用法相当于没有捕获异常,因此程序会直接崩掉/退出
小结:
- 如果没有出现异常,则执行try块中所有语句,不执行catch块中语句,如果有finally,最后还需要执行finally里面的语句
- 如果出现异常,则try块中异常发生后,try块剩下的语句不再执行。将执行catch块中的语句,如果有finally,最后还需要执行finally里面的语句
public class Homework {
public static void main(String[] args) {
Scanner myScanner = new Scanner(System.in);
int num = 0;
String str = "";
while(true){
str = myScanner.next();
try {
num = Integer.parseInt(str);
break;
} catch (NumberFormatException e) {
System.out.println("你输入的不是数字,请重新输入:");
}
}
System.out.println("你输入的数字为:" + num);
}
}
throw和throws的区别:
意义 位置 后面跟的东西
throws 异常处理的一种方式 方法声明处 异常类型
throw 手动生成异常对象的关键字 方法体中 异常对象
public class Homework {
public static void main(String[] args){
try {
if(args.length!=2){
throw new ArrayIndexOutOfBoundsException("参数个数不对");
}
int n1 = Integer.parseInt(args[0]);
int n2 = Integer.parseInt(args[1]);
double res = cal(n1 ,n2);
System.out.println("计算结果=" + res);
} catch (ArrayIndexOutOfBoundsException e) {
System.out.println(e.getMessage());
}catch (NumberFormatException e){
System.out.println("参数格式不正确,需要输出整数");
}catch (ArithmeticException e){
System.out.println("出现了除0的异常");
}
}
public static double cal(int n1,int n2){
return n1/n2;
}
}
包装类Wrapper(针对八种基本定义相应的引用类型):
基本数据类型 包装类
boolean----------------Boolean
char----------------Character
下面的包装类父类都是Number
byte----------------Byte
short----------------Short
int----------------Integer
long----------------Long
float----------------Float
double----------------Double
把三元运算符看作是一个整体,谁的优先级最高就用谁
Integer -128~127 存到数组 其它都是new
Integer i1 = 128;只要有基本数据类型,判断的是值相等
int i2= 128;
System.out.println(i1==i2);//true
String 类(是个类!!!)
字符串的字符使用 Unicode 字符编码,一个字符(不区分字母还是汉字)占两个字节 String 类实现了接口 Serializable【String 可以串行化:可以在网络传输】
接口 Comparable [String 对象可以比较大小]
String 是 final 类,不能被其他的类继承
String 有属性 private final char value[]; 用于存放字符串内容
一定要注意:value 是一个 final 类型, 不可以修改:即 value 不能指向新的地址,但是单个字符内容是可以变化
String创建对象:
- 直接赋值String s = “xxx”;
- 调用构造器 String s2 = new String(“xxx”);
s.equal()比较的是字符串内容 s.intern()是指向常量池的地址
StringBuilder和StringBuffer均代表可变的字符序列
效率: StringBuilder > StringBuffer > String
Math类:
随机数 a<= x <=b (int)(a + Math.rondom()*(b-a +1))
Arrays类:
sort重载的,也可以通过传入一个接口Comparator实现定制排序,实现了Comparator的匿名内部类,要求实现compare方法
因为数组是引用类型,所以通过sort排序后,会直接影响到实参
Date类在java.util包,默认输出的日期格式是国外格式,需要对格式进行转换
SimpleDateFormat sdf = new SimpleDateFormat("yyyy 年 MM 月 dd 日 hh:mm:ss E");
String format = sdf.format(d1); // format:将日期转换成指定格式的字符串
System.out.println("当前日期=" + format);
编程小技巧:写出正确的情况,然后取反即可
public class Homework {
public static void main(String[] args) {
String name = "zhao";
String pwd = "123456";
String email = "jack@sohu.com";
try {
userRegister(name,pwd,email);
System.out.println("恭喜你注册成功啦!!!");
} catch (Exception e) {
System.out.println(e.getMessage());
}
}//针对输入的内容进行校验,如果发现有问题,就抛出异常,给出提示
public static void userRegister(String name,String pwd,String email){
int userLength = name.length();
if(!(userLength >= 2 && userLength <=4)){
throw new RuntimeException("用户名长度为2或3或4");
}
if(!(pwd.length() ==6 && isDigital(pwd))){
throw new RuntimeException("密码长度为6,要求全是数字");
}
int i = email.indexOf('@');
int j = email.indexOf('.');
if(!(i > 0 && i < j)){
throw new RuntimeException("邮箱中包含@和. 并且@在.前面");
}
}
public static boolean isDigital(String str){
char[] chars = str.toCharArray();
for (int i = 0; i < chars.length; i++) {
if(chars[i] < '0' || chars[i] > '9'){
return false;
}
}
return true;
}
}
集合:
Collection实现子类可以存放多个元素,每个元素可以是Object
Collection接口没有直接的实现子类,是通过它的子接口Set和List来实现的
Collection接口常用方法,以实现子类ArrayList来演示:
- add:添加单个元素
- remove:删除指定元素
- contains:查找元素是否存在
- size:获取元素个数
- isEmpty:判断是否为空
- clear:清空
- addAll:添加多个元素
- containsAll:查找多个元素是否都存在
- removeAll:删除多个元素
单列集合:
Collection接口-定义了存取一组对象的方法,其子接口Set和List分别定义了存储方式。
Set中的数据对象没有顺序并且不可以重复
List的数据对象有顺序并且可以重复
双列集合(存放的 K-V):
Map接口定义了存储”键(key) - 值(value)映射对”的方法
Iterable里面有个重要的方法iterator, 通过该方法返回一个Iterator对象
Iterator对象称为迭代器,主要用于遍历Collection集合中的元素
所有实现了Collection接口的集合类都有一个iterator()方法,用以返回一个实现了Iterator接口的对象,即可以返回一个迭代器
//显示所有快捷键的快捷键ctrl + j
在调用iterator.next()方法之前必须要调用iterator.hasNext()进行检测。若不调用,且下一条记录无效,直接调用iterator.next()会抛出NoSuchElementException异常。
增强for循环,可以代替iterator迭代器:
for(元素类型 元素名:集合名或数组名){
访问元素
}
while (iterator.hasNext()){
System.out.println(iterator.next());
}
for(Object dog : list){
System.out.println(dog);
}
ArrayList是线程不安全的,可以看源码 没有synchronized
Vector底层结构和源码剖析
Set接口基本介绍:
- 无序(添加和取出的顺序不一致),没有索引
- 不允许重复元素,所以最多包含一个null
常用方法:
Set是Collection的子接口,因此常用方法和Collection接口一样
- 可以使用迭代器
- 增强for
- 不能使用索引的方式来获取
下图非常重要
Map接口:
Map接口的常用方法:
1.put:添加
2.remove:根据键值删除映射关系
3.get:根据键获取值
4.size:获取元素个数
5.isEmpty:判断个数是否为0
6.clear:清楚
7.containsKey:查找键是否存在
Map接口的遍历方法:
containKey:查找键是否存在
keySet:获取所有的键
entrySet:获取所有关系k-v
values:获取所有的值
//第一组: 先取出 所有的 Key , 通过 Key 取出对应的 Value
Set keyset = map.keySet();
//(1) 增强 for
System.out.println("-----第一种方式-------");
for (Object key : keyset) {
System.out.println(key + "-" + map.get(key));
}
//(2) 迭代器
System.out.println("----第二种方式--------");
Iterator iterator = keyset.iterator();
while (iterator.hasNext()) {
Object key = iterator.next();
System.out.println(key + "-" + map.get(key));
}
//第二组: 把所有的 values 取出
Collection values = map.values();
//这里可以使用所有的 Collections 使用的遍历方法
//(1) 增强 for
System.out.println("---取出所有的 value 增强 for----");
for (Object value : values) {
System.out.println(value);
}
//(2) 迭代器
System.out.println("---取出所有的 value 迭代器----");
Iterator iterator2 = values.iterator();
while (iterator2.hasNext()) {
Object value = iterator2.next();
System.out.println(value);
}
//第三组: 通过 EntrySet 来获取 k-v
Set entrySet = map.entrySet();// EntrySet<Map.Entry<K,V>>
//(1) 增强 for
System.out.println("----使用 EntrySet 的 for 增强(第 3 种)----");
for (Object entry : entrySet) {
//将 entry 转成 Map.Entry
Map.Entry m = (Map.Entry) entry;
System.out.println(m.getKey() + "-" + m.getValue());
}
//(2) 迭代器
System.out.println("----使用 EntrySet 的 迭代器(第 4 种)----");
Iterator iterator3 = entrySet.iterator();
while (iterator3.hasNext()) {
Object entry = iterator3.next();
//System.out.println(next.getClass());//HashMap$Node -实现-> Map.Entry (getKey,getValue)
//向下转型 Map.Entry
Map.Entry m = (Map.Entry) entry;
System.out.println(m.getKey() + "-" + m.getValue());
}
Collection c = new ArrayList();//父类的引用指向子类对象
ArrayList list = new ArrayList();//
动态绑定机制:
当调用对象方法时,该方法会和对象的内存地址/运行类型绑定(继承的查找关系)
当调用对象属性时,没有动态绑定机制,哪里声明,哪里使用
重写equals方法,必须重写hashcode方法
hashCode()方法:
1.提高具有哈希结构的容器的效率
2.两个引用,如果指向的是同一个对象,则哈希值肯定是不一样的
3.两个引用,如果指向的是不同的对象,则哈希值是不一样的
4.哈希值主要根据地址号来的,不能完全将哈希值等价于地址
容器类对象在调用remove、contains等方法时需要比较对象是否相等,这会涉及到对象类型的equals方法和hashcode方法;对于自定义的类型,需要重写equals和hashcode方法以实现自定义的对象相等规则
注意:相等的对象应该具有相等的hashcodes
hashcode什么时候用:当这个对象在map里面,一对一个叫key,另一个叫value,当对象作为键的时候是有用的
Interator对象做为迭代器,用以方便的实现对容器内元素的遍历操作,要求你返回一个实现了Iterator方法的对象
Iterator i = c.iterator();//拿到了HashSet本身提供的本身遍历自己的对象
while(i.hasNext()){
Name n = (Name) i.next();
System.out.println(n.getFirstName() + " ");
}
for(Iterator i =c.iterator();i.hasNext();){
Name name = (Name)i.next();
if(name.getFirstName().length()<3){
i.remove();
}
}
类 java.util.Collections提供了一些静态方法实现了基于List容器的一些常用算法
Comparble 接口:
所有可以”排序”的类都实现了java.lang.Comparable接口,Comparable接口中只有一个方法
public int compareTo(Object obj);
返回 0 表示 this == obj
返回正数表示 this > obj
返回负数表示 this < obj
List list = new ArrayList();
list.add("tom");
list.add("smith");
list.add("king");
list.add("milan");
Collections.sort(list,new Comparator(){
@Override
public int compare(Object o1, Object o2) {
return ((String)o1).length()-((String)o2).length();
}
});
System.out.println(list);
实现了Comparable接口的类通过实现comparaTo方法从而确定该类对象的排序方式
Array读快改慢、Linked改快读慢、Hash两者之间
Map类存储的键-值对通过键来标识,所以键值不能重复(是两个对象的equals不能相等,但用equals又效率太低了,所以比较hashcode())
在合适的时机自动打包、解包
自动将基础类型转换为对象(打包)
自动将对象转换为基础类型(解包)
泛型(Generic):
泛(广泛)型(类型) => Integer,String,Dog
- 泛型又称参数化类型,是jdk5.0出现的新特性,解决数据类型的安全性问题
- 在类声明或实例化时只要指定好需要的具体的类型即可
- Java泛型可以保证如果程序在编译时没有发出警告,运行时就不会产生ClassCastException异常。同时,代码更加简洁、健壮
- 泛型的作用是:可以在类声明时通过一个标识表示类中某个属性的类型,或者是某个方法的返回值的类型,或者是参数类型。
泛型只能是引用类型
在给泛型指定具体类型后,可以传入该类型或其子类型
如果我们这样写List list3 = new ArrayList() 默认给它的泛型是Object
在实际开发中,我们往往简写List<Integer>list = new ArrayList<>(),编译器会进行类型推断,推荐使用该写法
自定义泛型:
class类名<T,R...>{
成员
}
普通成员可以使用泛型(属性、方法)
使用泛型的数组,不能初始化
静态方法中不能使用类的泛型
泛型类的类型,是在创建对象时确定的(因为创建对象时,需要指定确定类型)
如果在创建对象时,没有指定类型,默认为Object
Interface 接口名<T,R....>{
}
接口中,静态成员也不能使用泛型(这个泛型类规定一样)
泛型接口的类型,在继承接口或者实现接口时确定
没有指定类型,默认为Object
JUnit
- JUnit是一个Java语言的单元测试框架
- 多数Java的开发环境都已经集成了JUnit作为单元测试的工具
多线程基础:
进程是指运行中的程序、线程时由进程创建的,是进程的一个实体、一个进程可以有多个线程
单线程:同一时刻,只允许执行一个线程
多线程:同一时刻,可以执行多个线程
并发:同一时刻,多个任务交替执行
并行:同一时刻,多个任务同时执行
在java中线程来使用有两种方法:
- 继承Thread类,重写run方法
- 实现Runnable接口,重写run方法
xx.start()启动线程->最终会执行cat的run方法
Java是单继承的,可以通过实现Runnable接口来创建线程
Thread thread = new Thread(dog);
thread.start();
这里底层使用了设计模式[代理模式]
线程常用方法:
注意事项和细节:
用户线程和守护线程:
- 用户线程:也叫工作线程,当线程的任务执行完或通知方式结束
- 守护线程:一般是为工作线程服务的,当所有的用户线程结束,守护线程自动结束
- 常见的守护线程:垃圾回收机制
XX.setDaemon(true)
Synchronized:
线程同步,即当有一个线程在对内存进行操作时,其他线程都i不可以对这个内存地址进行操作,直到该线程完成操作,其他线程才能对该内存地址进行操作
//这里使用synchronized实现了线程同步
//当多个线程执行到这里时,就会去争夺this对象锁
//哪个线程争夺到(获取)this对象锁,就执行synchronized代码块,执行完后,会释放this对象锁
//争夺不到this对象锁,就blocked,准备继续争夺
互斥锁:
线程死锁:
多个线程都占用了对方的锁资源,但不肯想让,导致了死锁,在编程是一定要避免死锁的发生
文件
文件流:
文件在程序中发是以流的形式来操作的
流:数据在数据源(文件)和程序(内存)之间经历的路径
获取文件的相关信息:
File 类的对象 还不能直接对文件进行读写操作,只能修改文件的属性
一个字符两个字节
节点流可以从一个特定的数据源读写数据,如FileReader、FileWriter
处理流(也叫包装流)是”连接”在已存在的流(节点流或处理流)之上,为程序提供更为强大的读写功能,如BufferedReader、BufferedWriter
类序列化类必须实现Serializable接口
网络:
ip地址(唯一标识主机):地址的组成=网络地址 + 主机地址,比如:192.168.16.69
A类:0-127 B类:128-191 C类:192-223 D类:224-239 E类:240-247
域名:将ip地址映射成域名,这里怎么映射,HTTP协议
端口号:用于标识计算机上某个特定的网络程序,表示形式(整数形式,0-65535 两个字节表示 2的16次方-1)
0-1024已经被占用,比如ssh 22,ftp 21,smtp 25,http 80(在网络开放中,不要使用0-1024)
常见的网络程序端口号:
tomcat:8080
mysql:3306
oracle:1521
sqlserver:1433
百度主机IP:110.242.68.3
百度域名:www.baidu.com
80端口:网站服务
25端口:邮件服务
8080端口:Tomcat服务
在网络编程中数据的组织形式就是协议
网络通信协议:
反射机制:
ClassLoader的类加载机制:
并非一次性加载
需要的时候加载(运行期间动态加载)
static语句块在加载后执行一次
dynamic语句块每次new新的对象都会执行:等同于构造方法中语句、用的较少