java基础
p460-470 List类子类的底层逻辑
JAVA基础
快捷键
ctrl+alt+L整理代码
shift+F10运行
ctrl+B定位找到方法
ctrl+H查看一个类的层级关系
ctrl+d复制当前代码项向下
shift+alt+上或下 将当前行代码上下移动
ctrl+f6重命名文件
ctrl+f 搜索
ctrl+j 显示所有快捷方式
Java要求
JAVA对大小写十分敏感,是强类型语言
一个源文件中最多只能有一个public类。其它类的个数不限
如果源文件包含一个public类,则文件名必须按该类名命名
一个源文件中最多只能有一个public类。其它类的个数不限,也可以将main方法写在非public类中,然后指定运行非public类,这样入口方法就是非public的main方法
注释(同C++)
单行注释==>//
多行注释==>/* */
文档注释 ==>/** */
参数信息:@author 作者名
@version 版本号
@since 指明需要最早使用的jdk版本
@param 参数名
@return 返回值情况
@throws 异常抛出情况
数据类型
数值类型
整数类型
整数
byte==>占1个字节范围:-128————127
short==>占2个字节范围:-32768-——————32767
int==>占4个字节范围:-214743648-——————2147483647
long==>占8个字节范围 Long类型要在数字后后面加个L
浮点类型 (小数)离散 舍入误差 大约 接近但不相等
float==>占4个字节范围 要在数字后后面加个F
double==>占8个字节范围
最好完全避免避免使用浮点数比较
银行业务表示钱不能用浮点数(浮点数误差),用BigDecimal 数学工具类
字符类型
单字符char==>占2个字节范围
String是字符串,但是String不是关键词,是类
boolean类型布尔值
占1为其值只有true和false两种
boolean flag = true;
引用类型
类
接口
数组
进制
二进制0b 0b1
八进制0 010
十六进制0x 0x10
类型转换
运算中,不同类型的数据先转化为同一类型,然后进行运算
强制转换 高==>低
自动转换 低==>高
转换的时候可能会内存溢出或者精度问题
低———————————————------------—————————>高
byte,short,char->int->long->float->double
String类型转为int使用Integer.parseInt( )
Object类型转String,在Object类型后面加一个"",双引号里不加任何东西
变量
局部变量(方法内部设变量)
实例变量(方法外部设变量)
从属于对象,如果不自行赋值则会输出默认值0 0.0 null
//布尔值的默认值是false
int age;
public static voif main(String[] args){
Demo08 demo08 = new Demo08();
System.out.println(demo08.age);
}//暂时不详细了解,平时用类变量代替
类变量(前面加上static,外部)
常量
final 常量名=值
final double PI=3.14;
可变参数
为避免方法重载过多,采用不定向参数
一个方法中只能指定一个可变参数,它必须是方法的最后一个参数。任何普通的参数必须在他之前声明
public void test(int... i){
}//由于不知道会有几个int数,所以直接采用可变参数
数组
int[] nums;//定义一个int类型的数组
nums = new int[10];//将这个数组设定为10个内容
int a[] = new int [5];//合并写法,等号前的括号可以在数组名的前或后都可以
获取数组长度方法 arrays.length
Arrays.sort(a); 把数组a排序(升序)
Arrays.toString(a);把a数组打印
Arrays.fill(a, val:0);用0将数组a填充
Arrays.fill(a,fromlndex:2, tolndex:4,. val:0);只填从2到4的位置,左开右闭
对于一个二维数组
map.length与map[ i ] . length不一样
前者是数列行的长度,后者是数列第i行的列的长度
赋值细节
数组的赋值是引用传递,赋的值是地址,赋值方式是引用赋值
把arr1赋值给arr2,相当于把arr1的地址赋值给arr2。如果arr2中的值发生改变,arr1的值也会改变
如果想进行数组拷贝,但是不是引用赋值
int [] arr2 = new int[arr1.length];//先根据arr1的长度创建一个数组,再用for循环挨个赋值
二维数组
二维数组的各个一维数组的长度可以不相等
二维数组的声明可以是[] [] y或y [] [] 或[] y []
二维数组开空间
int[][] arr = new int[3][];//先明确创建三个一维数组
arr[0] = new int[3];//给二维数组第一个一维数组开三个空间
冒泡排序
import java.util.Arrays;
public class 冒泡排序 {
public static void main(String[] args) {
int [] a={1,331,12,14,54,75,3};
int[] sort=sort(a);
System.out.println(Arrays.toString(sort));
}
public static int[] sort(int []array) {
int temp = 0;
for (int i = 0; i < array.length - 1; i++) {
//外层循环,一共有多少个数字就循环多少次
for (int j = 0; j < array.length - 1; j++) {
//内层循环,每进行一次下一次循环少进行一步
if (array[j + 1] > array[j]) {
temp = array[j];
array[j] = array[j + 1];
array[j + 1] = temp;
}
}
}
return array;
}
}
稀疏数组
如果一个数组里面的大部分元素为0或同一值;则可以采用此方法
处理方式:
1,记录数组一共有几行几列,有多少不同值。
2,把具有不同值的元素和行列及值记录在一个小规模的数组中
数组【0】用于记录行数列数与有多少不同值,余下数组记录值与他的位置
命名规范
1.只能由字母,数字,下划线,美元符号组成
2.不能以数字开头
3.区分同一个字母的大小写,即 hello 和 Hello 是不同的标识符
Java标识符一般有以下规范:
1.包名:全小写,如 java.awt.event, test
2.类名:首字母大写,如果由多个单词组成,要求每个单词的首字母都大写,如 HelloWorldApp
3.接口名:命名规则与类名相同。
4.方法名:往往由多个单词合成,第一个为动词,因此动词要全小写,后面的单词首字母大写,如
balanceAccount、isButtonPressed
5.变量名:一般为名词,全小写,如 area,lenth
6.常量名:全大写,如 MAX_INT, YEAR
7.对于变量名和方法,标识符不宜以“_” 和 “$” 为第一个字符,这两个字符对内部有特殊含义。
变量的命名规范
1.类成员变量:首字母小写和驼峰原则 monthSalary
2.局部变量:首字母小写和驼峰原则
3.常量:大写字母和下划线MAX_VALUE
4.类名:首字母大写和驼峰原则Man,GoodMan
5.方法名:首字母小写和驼峰原则 run(),runRun()
重载
一个方法可以写多种并且重名,每一种的参数体都不一样
缺省参数,定义函数时可以让最右边的连续若干个参数有缺省值
int func(int x1,int x2=2){ };
func(10)等效于func(10,2)
func(10,5)等效于func(10,5,2)
func(10, , 8)不可以,只能说最右边
new代码的理解(自己理解的)
Pet dog = new Pet();
对象类型 对象名=new 对象值
对象值实例化引出了他的一个对象dog,
dog含有对象值的属性和方法
堆中的dog会调用方法区中对象类型的方法
所以方法最终一般是与左边有关
对象类型的变化其实就是多态,其本质就是子类继承了父类的方法,所以子类的对象可以调用父类的方法
方法设置
修饰符 返回值类型 方法名(参数)
没有返回值,则写入void;可以不写return或者只写return
静态
和类一起加载的
在其他类调用时只需要打 类名.方法名 即可
非静态
实例化new后才存在
在其他类里调用时,先将其实例化再用静态的调用方法
类
类实例化后会返回一个自己的对象
只能用public和默认来修饰
类的五大成员:属性,方法,构造器,代码块,内部类
属
默认初始化:
数字:0 0.0
char:u0000
boolean:false
引用:null
属性格式:修饰符 属性类型 属性名=属性内容
修饰符public>default(默认)>protected>private
对象的9大方法
-
equals:判断两个对象的地址是否是同一个地址
-
getClass:反射获取类信息(返回一个对象所属的类)(获取它的运行类型)
-
toString:输出的是对象所属的类信息,以及对象地址
-
hashCode
-
notify
-
notifyAll
-
wait
-
wait重载
-
wait重载
除了这九个方法,其他的都是人为写的
构造器 public 类名()
一个类即使什么都不写,他也会存在一个方法
使用new,必须要有构造器,隐藏存在的是一个无参构造器。如果类里面有含参构造器,则使用无参构造器时无参构造器不能隐形
alt+insert快速生成一个构造器
要求
1.和类名相同
2.没有返回值
作用
1.new本质在调用构造方法(当同时包含有参构造和无参构造时,输入不同,调用的构造器不同)(程序走到new行代码后,会去类中找到复合的构造器并执行)
new Person();//调用无参构造
new Person(浩衡);//调用有参构造public Person(String name),浩衡就是输入构造器的name。此时在构造器中进行this.name = name;可以将浩衡赋给Person一个对象的属性name
2.初始化对象的值(非常重要,在无参构造器中通过this将类的属性赋予初始值)
注意点:
1.定义有参构造之后,如果想使用无参构造,显示的定义一个无参的构造
this.name代表这个类的一个属性name
访问修饰符
访问级别 | 访问控制修饰符 | 同类 | 同包 | 子类 | 不同包 |
---|---|---|---|---|---|
公开 | public | √ | √ | √ | √ |
受保护 | protected | √ | √ | √ | × |
默认 | 没有修饰符 | √ | √ | × | × |
私有 | private | √ | × | × | × |
只有默认和public可以修饰类
封装 可以隐秘的进行合法检查
提高程序安全性,保护数据
隐藏代码的实现细节
统一接口
系统可维护性增加
判断合法输入,权限验证等代码都输入在封装里面。
高内聚,低耦合
属性私有(private int a;),get/set
私有的属性不能用s1.name这样去调用对象s1中的属性name
可以操作私有的方法:
get获得这个数据
set给这个数据设置值
在s1类里面
public String getName()
{
return this.name;
}//设置获取数据的方法
public void setName(String name){
this.name=name;
}//设置数据
在主类里面
String name=s1.getName();
s1.setName("xx");
alt+insert可以快速设置get/set 选择getter and setter
越过检测
通过构造器,在实例化时就就把属性赋给对象,正常情况下可以跳过set里面的检测
解决方法:在构造器里调用set
&&&继承extends
在java中,所有类都直接或间接继承object类
a extends b;
public:子类继承父类的全部方法,属性(父类与子类有相同名字的属性时,子类.属性名调用的是子类的属性)
private无法继承
ctrl+H可以打开继承树
一般父类的属性采用private不进行继承,同时设置get/set,将这两个方法继承给子类。这样在实例化子类后,可以同时使用父类与子类的属性
java采用单继承,一个子类只能有一个父类。父类可以有多个子类。但是接口可以多继承
被final修饰的类不能有子类
super
this.xxx 调用该类里面的属性和方法
super.xxx 调用父类的属性与方法 私有无法被继承
调用父类的构造器必须在子类的第一行、
无参构造第一行有隐藏super,会调用父类的无参构造
父类没有无参,子类也不能有无参
super与this不能同时调用构造
this
调用本身
细节
子类必须调用父类的构造器,完成父类的初始化
当创建子类对象时,不管使用子类的哪个构造器,默认情况下总会去调用父类的无参构造器,如果父类没有提供无参构造器,则必须在子类构造器中用super指定使用父类的哪个构造器完成父类的初始化
&&&重写override 需要是非静态方法
重写都是方法的重写
避免重写就用static
父类的引用指向子类
父类 b=new 子类();
最终用的时子类
重写的修饰符范围可以扩大protected>default>public
静态方法:方法的调用只和左边的数据类型有关
所有参数要一直,方法名称也要相同。但是返回类型可以不一样,子类返回类型可以是父类返回类型或者他的子类
子类不能缩小父类方法的访问权限
&&&多态
重要的几句话
- 一个对象的编译类型和运行类型可以不一致
- 编译类型在定义对象时就确定了,不能改变
- 运行类型是可以变化的
- 编译类型看定义时=号左边,运行类型看=号右边
Animal animal = new Dog()
//animal是对象引用,new Dog()是对象
//animal编译类型是Animal,运行类型是Dog
重写和重载就体现了多态
一个对象的实际类型是确定的,但是他的指向类型是不确定的
多态是方法的多态,属性没有多态
要有父子类型才能进行高转低 ClassCastException类型转化异常
存在条件:继承关系,方法需要重写,父类引用指向子类对象 Father f1=new Son();
无法重写:
static 方法,属于类,不属于实例
final 常量
private方法不能重写
向上转型
本质:父类的引用指向了子类的对象
语法:父类类型 引用名 = new 子类类型( );
特点:编译类型看左边,运行类型看右边
可以调用父类中的所有成员
不能调用子类的特有成员
最终运行效果看子类的具体实现
编译(写代码)时看编译类型,运行结果看运行类型。所以调用属性,方法时只能找编译类型(父类的),运行结果看运行类型(先从运行类型的重写里面找,找不到就用父类的)
注意 属性的向上转型最后结果是看编译类型
动态绑定机制
- 当调用对象方法的时候,该方法会和该对象的内存地址/运行类型绑定
- 当调用对象属性时,没有动态绑定机制,哪里声明,哪里使用
public class DynamicBinding {
public static void main(String[] args) {
//a 的编译类型 A, 运行类型 B
A a = new B();//向上转型
System.out.println(a.sum());
System.out.println(a.sum1());
}
}
class A {//父类
public int i = 10;
//动态绑定机制:
public int sum() {//父类sum()
return getI() + 10;//20 + 10
}
public int sum1() {//父类sum1()
return i + 10;//10 + 10
}
public int getI() {//父类getI
return i;
}
}
class B extends A {//子类
public int i = 20;
// public int sum() {
// return i + 20;
// }
public int getI() {//子类getI()
return i;
}
// public int sum1() {
// return i + 10;
// }
}
解释:
调用a.sum(),由于是向上转型,a的运行类型是B,所以从B类开始找,但是B类sum方法被注释掉了,不存在。于是去找父类A找到了,就使用A类的sum方法,由于动态绑定机制,所以sum中的getI方法还是从B开始找。
调用a.sun1(),同理,最后用A类的sum1,由于属性没有动态绑定机制,所以sum1中的i调用的是所处类(A)的属性i
向下转型
语法:子类类型 引用名 = (子类类型)父类引用;
只能强转父类的引用,不能强转父类的对象
要求父类的引用必须指向的是当前目标类型的对象
向下转型后可以调用子类类型中所有的成员
Animal animal = new Cat;//向上
Cat cat = (Cat) animal;//向下
//此处animal原本就是指向Cat的,所以可以向下转型。不能向下转型到Dog
instanceof和类型转换
instance可以判断是否有父子关系;
看instanceof前面的对象的运行类型是不是后面类型或者他的子类
Person person = new Student();
后面是比较的类型,而前面展开来说是person类型的student对象,所以与Teacher无关系
System.out.println(person instanceof Student); True
System.out.println(person instanceof Person); True
System.out.println(person instanceof Object); True
System.out.println(person instanceof Teacher); False
//System.out.println(person instanceof String); person与string没有一点关系,无法比较
//学长理解:instance左边是对象右边是要比较的类,对象属于右边类或者它的子类时是true 形式上:对象 instanceof 类
类型转换
低转高不需要强制转换,但是子类转换为父类可能会失去一些自己本来的方法,所以如果以后要低转高,最好在实例化时就用高
Student student = new Student;
Person person = student;
Person obj = new Student( );
目前无法调用子类obj(Student)的方法,需要将obj这个对象转换为Student类型
转换方法: Student student = (Student) obj;
此时在输入student.go( );就相当于调用了obj对象所在的Student类中的go方法
或者直接 ( ( Student ) obj ).go;
多态数组
数组的定义类型为父类类型,里面保存的实际元素类型为子类类型
自定义一个类,就可以用这个类型建立数组,这个数组中可以放入他自己和他的子类的对象。各个对象的属性需要通过他们自己类里面的构造器放入数组中。
虽然没有放入方法的代码,但是可以直接调用对象的父类重写方法,并且符合动态绑定机制。比如Person[i]编译类型是Person,但是运行类型是要看传入数组时右边实例化的类的类型
调用子类特有方法:无法像父类重写代码一样直接调用。先用instanceof判断person[i]的运行类型是不是Student类型或者他的子类,如果是的话,就用向下转型为Student,于是就可以调用Student的特有方法
多态参数
方法定义的形参类型为父类类型,实参类型允许为子类类型
例子:
Worker和Manager都继承于Employee。 他们都重写了父类的getAnnual方法
在主程序里可以定义一个方法,参数为形参Employee e,方法里面调用getAnnual的话,由于动态绑定机制,他们可以调取各自的getAnnual方法
Object类
==
既可以判断基本类型,又可以判断引用类型
基本类型判断值,引用类型判断地址,即看是不是同一个对象(是不是指向同一个对象空间(等号左边指向右边)
Interger integer1 = new Integer(1000);
Interger integer2 = new Integer(1000);
//integer1 == integer2为假,因为1和2的Integer是new出来的,实际地址(对象空间)是不一样的
'A'==65 为真
equals
Objct类中的方法,只能判断引用类型
默认判断对象地址是否相等,子类种往往重写该方法,用于判断内容是否相等
重写equals
class Person{ //extends Object
private String name;
private int age;
private char gender;
//重写Object 的 equals方法
public boolean equals(Object obj) {
//判断如果比较的两个对象是同一个对象,则直接返回true
if(this == obj) {
return true;
}
//类型判断
if(obj instanceof Person) {//是Person,我们才比较
//进行 向下转型, 因为我需要得到obj的 各个属性
Person p = (Person)obj;
return this.name.equals(p.name) && this.age == p.age && this.gender == p.gender;//name.equals调用的是String里重写的equals,比较是不是同一类型的同时比较内容是否一样
}
//如果不是Person ,则直接返回false
return false;
}
//该equals可以判断两个对象是不是都是Person类的同时,判断三种属性是不是都一样。可以用来查重
hashCode(学完集合再深入)
5个小结论
- 提高具有哈希结构的容器的效率!
- 两个引用,如果指向的是同一个对象,则哈希值肯定是一样的!
- 两个引用,如果指向的是不同对象,则哈希值是不一样的
- 哈希值主要根据地址号来的!,不能完全将哈希值等价于地址。
- 后面在集合中hashCode如果需要的话,
也会重写
toString方法
默认返回:全类名(包名+类名)+@+哈希值的十六进制
子类往往重写toString用于返回属性
子类的重写可以用alt+insert快速调用
System.out.println(monster);
//等价于
System.out.println(monster.toString());
//当直接输出一个对象时,toString方法会被默认的调用
finalize方法
当对象被回收时,系统自动调用该对象的finalize方法。子类可以重写该方法,做一些释放资源的操作
回收:当某个对象没有任何引用时,则jvm就认为这个对象是一个垃圾对象,就会使用垃圾回收机制来销毁该对象,在销毁该对象前,会先调用finalize方法。
Car bmw = new Car("宝马");
bmw = null;//bmw变为空,就与Car断了联系,此时car对象就是一个垃圾,垃圾回收器就会回收(销毁)对象空间,在销毁前,会调用这个对象的finalize方法
//这时可以在finalize方法里面写一些自己想要执行的代码
同样可以直接用alt+insert重写
垃圾回收机制的调用是由系统决定的,但是也可以通过System.gc( )主动触发垃圾回收机制(但也不是百分之百,不会阻塞代码,相当于提醒系统回收)
实际开发中,几乎不会运用到finalize,更多是为了应付面试
main方法
- mian方法由java虚拟机调用,所以权限要设置为public
- 虚拟机在执行mian时不需要建立对象,所以用static
- args数组
- main是一个静态方法,这就是为什么对于非静态类要先实例化才能调用方法
- idea中可以动态传参
代码块
[修饰符]{
代码
}; 分号可以写也可以省略
和方法不同,没有方法名,没有返回,没有参数,只有方法体,而且不用通过对象或类显式调用,而是加载类时,或创建对象时隐式调用
注意
- 修饰符只能是static(静态代码块)或者没有(普通代码块)
相当于另外一种形式的构造器(对构造器的补充机制)
如果多个构造器有重复的语句,可以抽取到初始化块中,提高代码复用性
不管调用哪个构造器,创建对象都会先调用代码块里的内容
代码块调用的优先级高于构造器
细节:
-
静态代码块随着类的加载而执行,并且执行一次。普通代码块每创建一个对象就执行一次(加载不等于创建)
- 类什么时候被加载:
- 创建对象实例时(new)
- 创建子类对象实例时,父类也会被加载
- 使用类的静态成员时(静态方法,静态属性)
- 类什么时候被加载:
-
如果使用类的静态成员时,普通代码块并不会执行
-
调用顺序:
-
先调用静态代码块和静态属性初始化(他们优先级一样,如果都有,则按他们定义的顺序调用)
-
再调用普通代码块和普通属性的初始化(他们优先级也一样)
-
最后调用构造方法(构造器)
-
-
构造器最前面其实隐含了super()和调用普通代码块
所以顺序是先调用父类的构造器(同样进行这样的隐藏规则),然后调用自身的普通代码块,最后进行构造器中的内容
-
静态代码块只能直接调用静态成员,普通代码块都可以
static关键字详解
使用static去new类,可以保证资源只有一份
静态变量(类变量)
private static int age;//静态变量
private double score;//非静态变量
A a = new A();
System.out.println(a.score);
System.out.println(a.age);
System.out.println(A.age);//类变量
静态变量可以使用类变量的形式,所以一般静态变量都采用类变量形式,方便确实他是静态变量
静态变量在多线程中会用到
在类中用public static 修饰变量,就可以使这个类的所有对象共享
类变量可以通过对象或者类名(推荐)进行访问
类变量随着类的加载而创建的,所以即使没有创建对象实例也可以访问
定义语法:
访问修饰符 static 数据类型 变量名;(推荐)
static 访问修饰符 数据类型 变量名;
静态方法(类方法)
非静态要先new后调用,非静态方法可以调用静态方法
静态方法在本类中可以直接go( );调用,或者 类名 . go( );
静态方法只能调用静态方法或静态变量(静态方法与类一起先加载出来,而此时非静态变量还没有加载出来,所以不能调用)
普通成员方法都可以访问(静态与非静态)
创建方法与静态变量相同
使用场景:当方法中不涉及到任何和对象相关的成员.则可以将方法设计成静态方法提高开发效率。这样可以不实例化对象就调用方法
类方法中无法使用和对象有关的关键词,比如this和super
静态代码块(狂神)
代码块:在程序中直接加入{ },并在其中写代码叫做匿名代码块。心得对象一创建,会先走匿名代码块(有多少个对象就一共会执行多少次),再走构造器
静态代码块:static{ }类加载时就执行,且永久只执行一次(根据这些特性,常用来赋初始值)
静态导入包
正常使用生成随机数函数,每次都要写Math . random( )
为了减少输入量,可以像C++库(#include math.h)一样将Math所在的包导入到代码开头
import java.lang.Math;
如果想详细的把包里的某个方法调用就需要用到静态导入包
import static java.lang.Math.random;//多加了一个static
final
可以修饰类,属性,方法和局部变量
用法
- 不希望类被继承(修饰类)
- 不希望父类的某个方法被子类覆盖/重写(修饰方法)
- 不希望类的某个属性的值被修改(修饰属性)
- 不希望某个局部变量被修改(修饰局部变量)
细节:
- final修饰的量又叫常量,一般用XX_XX命名
- final修饰的属性在定义时,必须赋初值并且以后不能再修改。赋值可以在如下位置
- 定义时
- 构造器中
- 代码块中
- 如果final修饰的时静态属性,则不能在构造器中赋值
- final不能继承,但hi可以实例化对象
- 如果类不是final类,但是含有final方法,则该方法虽然不能重写,但是可以被继承
- 一个类已经是final类了,就没有必要再用final修饰方法了(这个类已经不能继承了)
- final不能修饰构造方法
- fianl常常和static搭配使用,效率更高(仅用static修饰属性,虽然类不会被创建但是也会被加载,final static修饰的则不会被加载)(两个修饰词的顺序可以颠倒)
- 包装类(Integer,Double,Float,Boolen)等都是fianl,String也是final
- 形参可以用final
抽象类
用abstract修饰可以形成抽象类,抽象方法;就是只有名字没有内容
抽象类的所有方法,继承了他的子类,都必须要去实现他的方法。除非这个子类也是抽象类
抽象类不能实例化,只能靠子类去实现它
抽象类里可以写普通方法,抽象方法必须在抽象类里
注意:abstract与final不能一起用,抽象需要继承,而final不允许继承。也不能与static连用,因为static不能被重写
抽象的目的就是设计架构,比如LOL中英雄各不相同,但是他们的属性类型(ad,ap)和技能组都是共同的。写好架构以后每一个英雄只需要跟着架构进行继承与重写,就能在固定的游戏玩法架构下成就多样性
接口的定义与实现 interface
接口不能写方法,抽象类里还可以写普通方法(jdk8.0后接口可以有静态方法,默认方法(default))
使用细节
- 接口也不能被实例化
- 接口中的所有定义都是抽象的,方法默认public abstract,属性默认是常量public static final(所以必须初始化)(可以不实例化直接访问)
- 一个普通类实现接口,要实现接口的所有方法
- 抽象类实现接口,可以不用实现接口的方法,快捷键Ctrl+i或者alt+enter
- 一个类可以是继承多个接口
- 接口不能继承其他类,但是可以继承多个别的接口
接口的多态
-
接口类型的变量可以指向实现了IF接口的对象实例,类似于父类指向子类
-
可以放入接口数组中,也就是多态数组
-
接口存在多态传递现象:a实现了b接口,b接口继承了c接口,则a实例化时可以向上转型为b或c(相当于teacher也实现了c接口)
接口都需要有实现类,通常用i(i大写)mpl结尾 实现类的关键字是implements
声明接口的关键词时interface
用法
要管理多个数据库,经理定义一个接口,可以规范程序员方法,变量的名字,同时可以方便调用
调用:多种数据库代码重写接口,定义一个方法,参数是接口类的类型,方法中调用抽象类的方法。则如果将某重写类传入这个方法中,就可以调用这个类重写的方法。
N种内部类
在一个类中再定义一个类,就是内部类
一个java类中可以有多个class但是只能有一个public class
最大特点是可以直接访问内部属性,并且可以体现类与类之间的包含关系
分为两类
- 定义在外部类局部位置上(比如方法内)
- 局部内部类
- 匿名内部类
- 定义在外部类的成员位置上
- 成员内部类
- 静态内部类
局部内部类(有类名)
通常是在方法里,也可以是代码块等
- 可以直接访问外部类的所有成员,包含私有的
- 不能添加访问修饰符,因为他的地位就只一个局部变量(存在于方法内部的变量),但是可以使用final修饰(局部变量也可以)
- 作用域:仅仅在定义它的方法或代码块中
- 访问内部类的方法:外部类可以实例化内部类然后调用内部类的方法(必须在作用域内)
- 外部其他类不能访问局部内部类
- 如果外部类和局部内部类的成员重名,默认遵循就近原则,如果想在局部内部类中访问访问外部类的成员可以使用this。外部类名. this可以理解为外部类的对象,谁调用了含有this的方法,这个对象就是谁
匿名内部类(没有类名)
1)本质是类 2)是内部类 3)还是一个对象
细节
- 可以直接访问外部类的所有成员,包含私有的
- 不能添加访问修饰符,因为他的地位就只一个局部变量
- 作用域:仅仅在定义它的方法或代码块中
- 匿名内部类访问外部类成员可以直接访问
- 外部其他类不能访问匿名内部类(因为是局部变量)
- 重名,就近原则
应用场景:想使用IA接口并创建对象。传统方法血药写一个新类重写接口,然后实例化这个接口。但是如果只是用一次的话传统方法太过麻烦,可以使用匿名内部类来简化
IA tiger = new IA(){
//实现IA类
};
//IA写在Outer类的method方法中
tiger的编译类型是IA,运行类型是匿名内部类。
实际上底层会先创建一个Outer$1(系统分配)的类实现接口(隐藏),表面上是把这个类实例化为tiger。
匿名内部类只能使用一次(Outer$1这个类在实例化后就不存在了,不能实例化出第二个对象),而匿名内部类生成的对象可以无限次调用
匿名内部类可以用于接口,也可以用于抽象类
进一步理解
Father father = new Father("jack"){};
如果没有大括号,那么father的运行类型就是Father。加上大括号,运行类型就是一个匿名内部类(类名是系统分配的),这个类里的代码就是大括号里的代码
因为本质是创建一个对象,所以最后加分号
调用方式
由于匿名内部类即使类又是方法,所以有两种调用方法
作为类:用对象接收(实例化),然后调用这个对象的方法
作为对象:直接
new Person(){
//重写代码hi
}.hi();
//hi()前面的一堆实际上是个对象
实际用法
匿名内部类可以当作实参直接传递
这样的话,如果方法的形参是接口类型的话,就可以不用新建一个类去实现接口,直接在匿名内部类中实现
成员内部类(没用static)
定义在外部类的成员位置
- 可以直接二访问外部类的所有成员,包括私有的
- 由于他的地位是一个成员,所以可以用四种修饰符修饰
- 作用域和外部类的其他成员一样
- 成员内部类访问外部类可以直接访问
- 外部类访问内部类要先创建对象后访问
- 其他外部类访问成员内部类有三种方法(第三种是第二种的改版,把两个实例化语句合并)
//方法一
Outer outer = new Outer();//先实例化外部类
Outer.Inner inner = outer.new Inner();//在实例化成员内部类
//方法二:在外部类中编写一个方法,可以返回成员内部类对象
public Inner getInnerInstance(){
return new Inner();
}
Outer outer = new Outer();
Outer.Inner innerInstance = outer.getInnerInstance();//此处是方法返回对象,相当于已经实例化过了,所以不用new
- 外部类与内部类成员重名,就近原则,访问外部类用this
内部类可以获得外部类的私有属性和私有方法
静态内部类(使用static)
public static class//生成内部类
- 由于比外部类先生成所以无法获得外部类的东西,但可以直接访问外部类的所有静态方法,包括私有的
- 可以添加四种修饰符
- 作用域同其他成员,为整个类体
- 静态内部类访问外部类可以直接访问
- 外部类访问静态内部类需要创建对象再访问
- 外部其他类访问静态内部类
//方式一,静态内部类是可以直接通过类名访问的
Outer outer = new Outer();
Outer.Inner inner = new Outer. Inner();
//方式二,写一个方法返回静态内部类对象,与成员内部类一样
- 就近原则,访问外部类成员用外部类名.成员。此处不能用this,因为这个访问的成员是静态的(细节1)
枚举类enum
枚举是一组常量的集合,属于一种特殊的类,里面只包含一组有限的特定对象
enum不能继承其他类,因为他已经继承了Enum类
枚举类跟其它类一样,也可以实现接口
枚举的实现方式:
- 自定义类实现枚举
- 使用enum关键词实现枚举
使用enum关键词实现枚举注意事项:
- 使用enum关键字开发一个枚举类时,默认继承Enum类,而且是一个final类
- 必须要说明用哪个构造器
- 如果使用无参构造器,创建常量对象时可以省略(),相当于直接写常数名
- 枚举对象必须放在枚举类的行首
- 可以把枚举常量赋给其他对象
Gender boy = Gender.BOY;
Gender boy2 = Gender.BOY;
//Gender枚举类中有BOY常量,该语句是在其他类中的
sout(boy);//本质就是调用Gender类的父类:Enum类的toString,他的方法内容是返回name(枚举常量名)
sout(boy2==boy)//相等(枚举常量是静态性质的,只有一个)
自定义枚举实现
- 将构造器私有化,防止被实例化,成为只读
- 去掉set的方法,只能读不能修改,提供get方法
- 在类内部直接创建固定的对象
- 枚举对象通常全部大写
- 可以重写toString
class Season{
public static final Season SPRING = new Season(使用私有构造器进行初始化);
}
//public使外部可以访问,static使外部可以直接访问,final防止类提前加载(属于优化)
//类的外部其他类就可以直接Season.SPRING直接访问
使用enum关键词实现枚举
- 使用关键词enum替代class
- 构造器,get方法,toString等都可以保留,要改变对象创建的方法
public static final Season SPRING = new Season(使用私有构造器进行初始化);
//代替为
SPRING(使用私有构造器进行初始化);//常量名(实参列表)
- 如果有多个常量(对象),使用,间隔
SPRING(使用私有构造器进行初始化),
SUMMER(使用私有构造器进行初始化),
AUTOMUN(使用私有构造器进行初始化);
- 要将定义的常量对象写在最前面,对象属性也要写在定义后面
Enum成员方法
由于枚举类继承了Enum,所以可以使用它的成员方法
- name建议使用toString
- ordinal输出的是该枚举对象的编号(从0开始编号,可以知道是第几个创建的常量)
- values,通过枚举类使用(不是常量),可以返回一个数组,含有定义的所有枚举对象(不是对象名,而是对象本身,数组类型与枚举类类型相同)
- valueOF将字符串转换为枚举对象,要求字符串必须为已有的常量名,否则会报错
- compareTo,比较两个枚举常量,比较的就是位置号 返回值为两者位置号的差值
注解Annotation
注解也被称为元数据,用于修饰包,类,方法,属性,构造器,局部变量等数据信息
注解不应行啊程序逻辑但是可以被编译或运行,相当于嵌入在代码中的补充信息
JAVAEE中使用的较多
写与不写其实结果一样,只是为了说明
jdk5.0加入了接口注解
@Target是修饰注解的注解,成为元注解
三种注解
@Override重写
限定某个方法是重写父类,该注解只能用于方法
如果写了,编译器会检查是否真的重写了父类的方法,如果是假的则会报错(相当于是语法校验)
@Deprecated过时
表示某个程序元素(类,方法等)已过时
可以修饰方法,类,字段,包,参数等等
可以做到新旧版本的兼容和过渡
过时不代表不能用,是不推荐使用
@SuppressWarnings抑制警告
可以让编译器给出的黄色警告不显示
@SuppressWarnings({" "}),双引号中写入相一致的警告类型(要查)
作用范围和放置的位置有关
异常
防止因为一个不算致命的异常导致整个系统崩溃
如果进行了异常处理,那么即使出现了异常,程序也可以继续指向
e.getMessage( )可以返回异常信息
ERROR一般是程序无法控制的,是灾难性的错误。Exception是可以被程序处理的,分为两种:运行时异常(编译器没有提示,要自己判断)和编译时异常
运行异常默认是抛出
异常处理
五个关键字:try catch finally throw throws
try{//try监控区域
代码;
}catch(异常类型 命名){//捕获异常,可以并列着写多个异常,但是要按从小到大排序
打出一些句子来提醒自己;
}finally{//不管程序出不出现异常,都会执行,程序崩掉前也会执行
代码;
}
//这样程序就不会报错。 try catch必须有,finally可选(通过关闭代码可以保证程序会关闭)
Ctrl+Alt+T可以快速生成catch
e(这是个名字).printStackTrace();//可以打印错误的栈信息
可以用来判断输入的是否为整数(Integer.parseInt能把字符串变为int,但是有异常)
抛出异常
throw new 异常类型();//主动抛出异常
一般写在方法里,假设这个程序处理不了,可以在方法上抛出异常
throws写在方法后public void test(int a) throws 异常类型;再用try catch接收
把异常抛给父类,父类可以选择继续向上抛或者trycatch。最高级为JVM。JVM处理异常方法:1.输出异常信息 2.退出程序
不做处理默认是抛出,实际上不做处理运行后中断就是因为JVM输出后退出了程序
自定义异常
继承Expection类,这个类就是自定义编译异常类
继承RuntimeException,属于运行异常
可以用来判断合法输入
一般自定义运行异常,因为编译异常还要throws
throws写在方法声明处,后面跟异常类型。throw写在方法体中,后面跟异常对象
常用类
包装类
包装类和基本数据的转换
以int与Integer为例
装箱:基本数据类型到包装类。 拆箱:包装类到基本数据类型
jdk5之前是手动装箱拆箱
//手动装箱
int n1 = 100;
Integer integer = new Integer(n1);\
//或者
Integer integer1 = Integer.valueOf(n1);
//手动拆箱
int i = integer.intValue();
jdk5后可以自动装箱拆箱
int n2 = 200;
//自动装箱
Integer integer2 = n2;
//自动拆箱
int n3 = integer2;
包装类型与String类型相互转换
Integer i = 100;//自动装箱
//包装类转String
//方式1
String str1 = i + "";
//方式2
String str2 = i.toString();
//方式3
String str3 = String.valueOf(i);
//String转包装类
String str4 = "12345";
//方式1
Integer i2 = Integer.parseInt(str4);
//方式2
Integer i3 = new Integer(str4);
只要有基本数据类型,那么==判断的就是数值
String
字符串的字符使用Unicode字符编码,一个字符(不区分字母还是汉字)占两个字节
String s = 字符串//直接赋值
String s1 = new String();//空的字符串对象
String s2 = new String(字符串);//初始化有内容的
String s3 = new String(char[] a);//数组转字符串对象
String s4 = new String(char[] a,int startIndex,int count);//输入开始位置与选取多少个数组元素
//还有许多其他的构造器
String实现了Serializable,说明String可以串行化(可以在网络里传输),实现了Comparable,说明可以比较。String是final类,不可以被继承。
String有一个属性是数组,用来存放字符串内容,这个数组是final类型的,赋值后不可以修改(指的是地址,不能指向新的地址,但是可以修改他的内容(在原来的地址处修改内容)。)
String s = "ABCabc";
System.out.println("s = " + s);
s = "123456";//正常
System.out.println("s = " + s);
string s2 = "123";
s = s2;//报错
注意,s只是这个对象的一个引用,s=123456执行后,又创建了一个新的对象,而s引用指向了这个新的对象,原来的对象仍然存在。所以多次使用这种修改方式会生成很多垃圾
方法
- equals区分大小写,判断内容是否相等
- equalsIgnoreCase忽略大小写的判断内容是否相等
- length获得字符的个数,字符串的长度
- indexOf获取字符在字符串中第一次出现的索引,索引从0开始,找不到返回-1(也可以查字符串)
- lastIndexOf获取字符在字符串中最后一次出现的索引,索引从0开始,找不到返回-1
- substring截取指定范围的字串
- trim去前后空格
- charAt获取某索引处的字符,注意不能使用Str[index]这种方法
- toUpperCase全转为大写
- toLowerCase全转为小写
- concat拼接字符
- replace替换字符串中的字符,把所有的xx替换为xx。返回的结果才是替换过的
- split分割字符串,返回值放入一个字符串数组中。对于 |,\\等需要用转义字符,想要以\\进行分割,就要输入\\\\,13为转义
- compareTo比较字符串大小,相同返回0,否则返回正数负数。(原理:先得到两个字符串最小长度,看最小长度内的字符串是否相同,相同的话返回值为两个字符串长度差值,如果不相同,返回的是第一次出现差异位置的字母插值)
- toCharArray转换为字符数组
- format将一系列类型的数据组合在一起,组合的方式与C++中printf一致
StringBuffer类
很多方法与String相同,但是长度可以变
StringBuffer是一个容器
String的value数组在堆中,内容在常量池中,每次修改都会在常量池中新建,并且更改指向
StringBuffer的value数组在堆中,内容也在堆中,并且这个数组可以扩容与修改内容。不用每次都创建新对象,效率高
构造器
StringBuffer stringBuffer = new StringBuffer();//创建一个大小为16的char数组
StringBuffer stringBuffer = new StringBuffer(int capacity);//创建一个大小为capacity的char数组
StringBuffer(String string);//创建一个大小为string长度+16的char数组
方法
- append追加
- delete(start,end)删除,左闭右开
- repalce(start,end,string)替换
- indexOf查找第一次出现的位置,找不到返回-1
- insert插入
- length
String与StringBuffer转换
String转StringBuffer
- 使用要求输入字符串的构造器
- 先使用无参构造器创建对象,然后使用append方法追加
StringBuffer转String
- 使用toString方法
- 使用String构造器,直接传入
StringBuilder
不是线程安全的
作为StringBuffer的一个简易替换
单线程优先使用该类,效率最高
Math
都是静态方法
- abs绝对值
- pow求幂
- ceil向上取整,返回该参数的最小整数(转成double)
- floor向下取整,返回该参数的最大整数(转成double)
- round四舍五入(转成long)
- random求随机数,返回的是0到1的随机数(可以为0)
- max求两个数最大值
- min求两个数最小值
Arrays类
方法
- toString返回一个字符串,能把数组内容拼接为一个字符串
- sort有自然排序与定制排序,会直接影响到实参。sort可以重载。定制排序实现了Comparator接口的匿名内部类,要求实现compare方法,方法返回值的正负会影响排序方式
- binarySearch通过二分搜索查找,要求必须排好序,如果不存在就返回-(low+1),low是小于输入数的第一个数的位置
- copyOf从arr数组中拷贝,有返回值。输入的长度如果大于arr长度,用null补全。
- fill填充数组
- equals比较两个数组元素内容是否完全相同
- asList将一组值转换成list集合
BigInterger/BigDecimal
前者适合保存比较大的整数,后者适合保存精度较高的浮点数
方法
不能正常使用加减乘除符号,要使用方法
- add加
- subtract减
- multiply乘
- divide除,BigDecimal 可能会出现异常(结果为无限循环小数)。解决方法:divide方法中输入第二个参数作为精度,输入BigDecimal.ROUND_CEILING,如果有无限循环小数,就会保留分子的精度
System类
方法
- exit退出当前程序,一般主动退出都是填0
- arraycopy赋值数组元素,比较适合底层调用。一般用Arrays类的copyOf
- currnetTimeMillens返回当前时间距离1970/1/1午夜的差异,以毫秒为单位
- gc运行垃圾回收机制
日期类
第一代 Date
//1. 获取当前系统时间
//2. 这里的Date 类是在java.util包
//3. 默认输出的日期格式是国外的方式, 因此通常需要对格式进行转换
Date d1 = new Date(); //获取当前系统时间
System.out.println("当前日期=" + d1);
Date d2 = new Date(9234567); //通过指定毫秒数得到时间
System.out.println("d2=" + d2); //获取某个时间对应的毫秒数
//
//1. 创建 SimpleDateFormat对象,可以指定相应的格式
//2. 这里的格式使用的字母是规定好,不能乱写.网上查
SimpleDateFormat sdf = new SimpleDateFormat("yyyy年MM月dd日 hh:mm:ss E");
String format = sdf.format(d1); // format:将日期转换成指定格式的字符串
System.out.println("当前日期=" + format);
//1. 可以把一个格式化的String 转成对应的 Date
//2. 得到Date 仍然在输出时,还是按照国外的形式,如果希望指定格式输出,需要转换
//3. 在把String -> Date , 使用的 sdf 格式需要和你给的String的格式一样,否则会抛出转换异常
String s = "1996年01月01日 10:20:30 星期一";
Date parse = sdf.parse(s);
System.out.println("parse=" + sdf.format(parse));
第二代 Calendar
//1. Calendar是一个抽象类, 并且构造器是private
//2. 可以通过 getInstance() 来获取实例
//3. 提供大量的方法和字段提供给程序员
//4. Calendar没有提供对应的格式化的类,因此需要程序员自己组合来输出(灵活)
//5. 如果我们需要按照 24小时进制来获取时间, Calendar.HOUR ==改成=> Calendar.HOUR_OF_DAY
Calendar c = Calendar.getInstance(); //创建日历类对象//比较简单,自由
System.out.println("c=" + c);
//2.获取日历对象的某个日历字段
System.out.println("年:" + c.get(Calendar.YEAR));
// 这里为什么要 + 1, 因为Calendar 返回月时候,是按照 0 开始编号
System.out.println("月:" + (c.get(Calendar.MONTH) + 1));
System.out.println("日:" + c.get(Calendar.DAY_OF_MONTH));
System.out.println("小时:" + c.get(Calendar.HOUR));
System.out.println("分钟:" + c.get(Calendar.MINUTE));
System.out.println("秒:" + c.get(Calendar.SECOND));
//Calender 没有专门的格式化方法,所以需要程序员自己来组合显示
System.out.println(c.get(Calendar.YEAR) + "-" + (c.get(Calendar.MONTH) + 1) + "-" + c.get(Calendar.DAY_OF_MONTH) +" " + c.get(Calendar.HOUR_OF_DAY) + ":" + c.get(Calendar.MINUTE) + ":" + c.get(Calendar.SECOND) );
问题:
- 可变性:像日期和时间这样的类应该是不可变的
- 偏移性:Date中的年份是从1900开始的,而且月份都是从0开始的
- 格式化:格式化只对Date有用,Calendar不行
- 他们不是线程安全的,不能处理闰秒等(每隔两天,多出1s)
第三代
LocalDate日期,年月日
LocalTime时间,时分秒
LocalDateTime日期时间,年月日(最常用)
Instant时间戳
LocalDateTime方法
- now返回当前日期时间的对象
- getYear/getMonth/getMonthValue(得到数字)/getDayOfMonth......
- DateTimeFormatter的对象可以进行格式化
- format,采用格式对象进行格式化,返回修改后的字符串
- toInstant可以把date转为Instant
DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
LocalDateTime ldt = LocalDateTime.now();
//提供 plus 和 minus方法可以对当前时间进行加或者减
//看看890天后,是什么时候 把 年月日-时分秒
LocalDateTime localDateTime = ldt.plusDays(890);
//看看在 3456分钟前是什么时候,把 年月日-时分秒输出
LocalDateTime localDateTime2 = ldt.minusMinutes(3456);
Instant方法
- now
- from,可以把instant对象转成Date
集合
数组缺点:
1)长度开始时必须指定,而且一旦指定,不能更改
2)保存的必须为同一类型的元素
3)使用数组进行增加/删除元素的示意代码-比较麻烦
集合优点:
1)可以动态保存任意多个对象,使用比较方便!
2)提供了一系列方便的操作对象的方法:add、
get,set、remove等
3)使用集合添加,删除新元素的示意代码-简洁了
集合主要分为两组:单列集合,双列集合
Collenction接口有两个重要的子接口List Set,他们的实现子类都是单列集合
Map接口的实现子类是双列集合,存放的K-V
Collection接口实现类特点
- 可以存放多个元素,每个元素可以是Object类
- 有些可以存放重复的元素,有些不可以
- 有些是有序的(List),有些是无序的(Set)
- Collection接口没有直接的实现子类,是通过他的子接口List,Set实现的
Collection接口常用方法
以实现子类ArrayList来演示
1)add:添加单个元素
2)remove:删除指定元素
3)contains:查找元素是否存在
4)size:获取元素个数
5)isEmpty:判断是否为空
6)clear:清空
7)addAIl:添加多个元素
8)containsAll:查找多个元素是否都存在
9)removeAll:删除多个元素
10)说明:以ArrayList实现类来演示
Collection接口遍历元素方法
1.使用迭代器Iterator
所有实现了Collection接口的集合类都有一个iteratorO方法,用以返回一个实现了Iterator接口的对象,即可以返回一个选代器
Iterator仅用于遍历集合,本身并不存放对象
原理:while中使用hasNext方法判断是否还有下一个元素,循环中通过next方法可以让指针下移并将下移以后集合位置上的元素放回。如果比用hasNext判断,当next下移到无效记录时会抛出异常
快速生成迭代器while,输入itit,回车
如果想再次使用,给本来的迭代器对象再赋值一次iterator方法
2.增强for
可以理解为简化版的迭代器
语法:
for(元素类型 元素名 : 集合名或数组名){
访问元素
}
快速生成:输入大写I,回车
List接口
- List集合类中元素有序(即添加顺序和取出顺序一致)且可重复
- List集合中的每个元素都有其对应的顺序索引,即支持索引。
- List实现子类中常用的是ArrayList,LinkedList,Vector
方法
- add
- addAll
- get 获取指定index位置的元素
- indexOf
- LastIndexOf
- remove
- set指定index位置的元素为ele(两个参数)
ArrayList
- all permits including null,Arraylist可以加入null,并且 多个
- ArrayList是由数组来实现数据存储的
- ArrayList基本等同于Vector,除了ArrayList是线程不安全(执行效率高)看源码,在多线程情况下,不建议使用ArrayList
LinkedList
- LinkedList实现了双向链表和双端队列特点
- 可以添加任意元素(元素可以重复),包括null
- 线程不安全,没有实现同步
如何选择ArrayList和LinkedList
1)如果我们改查的操作多,选择ArrayList
2)如果我们增删的操作多,选择LinkedList
3)一般来说,在程序中,80%-90%都是查询,因此大部分情况下会选择ArrayList
4)在一个项目中,根据业务灵活选择,也可能这样,一个模块使用的是ArrayList,另外一个模块是LinkedList.
Set接口
- 无序(添加的顺序与取出的顺序不一样),没有索引
- 不允许重复元素,所以最多包含一个null
- 常用的有HashSet,TreeSet
常用方法与collection一样
遍历方式可以使用迭代器或增强for,但是不能使用普通for(不能使用索引的方式来获取)
HashSet
- 实现了Set接口
- 底层实际上是HashMap
JAVA绘图
定义一个MyPanel类,继承JPanel
重写paint方法,保留里面的super(调用父类有参构造器完成初始化)。Graphics可以理解为画笔类,里面有很多方法
paint会在以下情况下被调用
- 组件第一次在屏幕显示
- 窗口最小化再最大化
- 窗口的大小发生改变
- repaint函数被调用
有趣的事情
JDK7新特性,数字直接可以用下划线分割,且不会被输出
int a=10;
int b=20;
System.out.println(""+a+b);
//输出结果为1020;
num--+
输出时"人数为"+num-- +"个",num--意思是每输出一次num-1
split拆分字符串
String[] split = Line.split("=");
//意思是把字符串Line分裂开,分裂的依据是=,分裂的部分会分别放入split数组的各个元素中
在字符串中输入双引号
正常情况下在定义字符串的双引号中写下双引号,\n等会影响字符串输出形式
在“前输入\可以告诉程序这个是字符串的一部分,其他同理
增强for循环
int[] nums = {1,2,9};
for(int i : nums){
sout(i);
}
//把nums数组一次取出赋给i(跟python的for一样)
三元运算符
Object obj1 = true?new Integer(1) : new Double(2.0);
sout(obj1);
//三元运算符,如果为真选前面,如果为后选后面
//这道题输出的结果为1.0
//三元运算符要看作一个整体,这个整体的最高精度为Double,所以1也是按照double形式输出的