1、byte,short,int,long,float,double,char,boolean
2、if,else,switch,case,default,for,while,do,break,continue
3、class
4、new
5、return
6、void
7、package,import
8、extends
9、this,super
10、instanceof
11、public,protected,private
12、static,final,native,abstract
错误的答案:String,main,System,println 都不是
1、权限修饰符:
public,protected,缺省,private
2、static
3、final
4、native
5、abstract
public,缺省,final,abstract
它们中final与abstract不能一起修饰类。
public,protected,缺省,private,static,final,native,abstract
它们中final和abstract不能一起修饰方法
它们中static和abstract不能一起修饰方法
它们中native和abstract不能一起修饰方法
它们中private和abstract不能一起修饰方法
public,protected,缺省,private,static,final
final
public,protected,缺省,private
总结:(1)构造器的修饰符只有权限修饰符。
(2)如果一个类没有编写构造器,它有一个默认的无参构造,
这个默认的无参构造的权限修饰符默认和类的权限修饰符一样。
如果手动编写构造器,就可以自己选择权限修饰符。
static
(1)单词,意思
(2)它可以修饰什么
(3)它修饰后有什么不同
day11:
1、单词native,意思是本地的,原生的
2、它可以修饰:方法
3、它修饰的方法有什么不同?
它修饰的方法的方法体不是Java语言实现的。而是由C/C++语言实现的,然后编译为.dll文件由Java语言调用。
(1)这种方法运行时,在本地方法栈中开辟空间
(2)native方法的方法体虽然是C/C++实现,但是在Java代码中和Java方法一样调用即可。
即如果是静态方法,那么使用类名.调用,如果是非静态方法,那么使用对象名.调用,
如果是私有的方法,那么只能在本类中调用。
(3)native方法,如果没有final修饰,可以被子类重写
JVM的内存的结构:
(1)方法区
(2)堆
(3)虚拟机栈
(4)本地方法栈
(5)程序计数器
public class Native {
public static void main(String[] args) {
//创建Object类的对象
Object obj = new Object();
System.out.println(obj.hashCode());//1163157884
System.out.println(obj);//java.lang.Object@4554617c
/*
JVM不对外暴露内存地址,显示的是对象的信息:
(1)运行时类型
(2)@符号
(3)这个对象的hashCode值的十六进制形式
十进制:1163157884
十六进制:4554617C
*/
Student stu = new Student();
System.out.println(stu.hashCode());
}
}
class Student extends Object{
private String name;
private int score;
//先不管逻辑意义,它是重写了Object类中hashCode方法
@Override
public int hashCode() {
return 1;
}
}
String,System,Math,Object等,这些类型都在java.lang包下。
用java.lang包下的类的时候,我们不需要import导包,就可以直接使用简名称。
API:专业的解释,应用程序编程接口(Application Programming Interface)。
通俗的讲,开发人员使用JRE核心类库的帮助文档,说明书。
每一套组件都会有自己的API,例如:后面学习的框架,第三方的工具都有自己的API。
包括我们自己写的代码,也可以提供专门的API。(如何提供,后面讲)
day11:
(1)类 Object 是类层次结构的根类。每个类都使用 Object 作为超类。
==> ①所有类都直接或间接的继承了Object
②当我们编写某个类时,没有显式的用extends指明它的直接父类是谁,那么它的直接父类就是Object
③Object类型的变量可以引用/指向/存储/接收 任意类型的对象
Object类型的数组,可以存储各种数据类型的对象。
④所有对象的实例初始化,都会最终调用到Object的实例初始化方法
⑤Object类的初始化应该是比所有都早
查看类的继承关系的快捷键:Ctrl + H(hierarchy继承树)
(2)所有对象(包括数组)都实现这个类的方法。
==>Object类中拥有的方法,都会被继承到所有的对象中。
Java的引用数据类型:类、数组、接口、枚举…
但是我们今天讲解5个。
①建议所有子类都重写。返回一个简明但易于读懂的信息表达式。
②如果不重写,默认情况下,由类名、at 标记符“@”和此对象哈希码的无符号十六进制表示组成
特别说明:
A:当我们使用System.out.println()方法打印一个对象时,默认就调用它的toString(),来返回这个对象的信息,而不是这个对象的地址
B:当我们一个对象与String进行“+”时,也会自动调用这个对象的toString()
说明:Class>,是一种数据类型,C大写。这种数据类型我们在后面的反射章节会重点讲。今天知道它代表一种类的类型。
当垃圾回收器确定不存在对该对象的更多引用时,由对象的垃圾回收器调用此方法。子类重写 finalize 方法,以配置系统资源或执行其他清除。
解释:①finalize()不是由程序员手动调用的,而是由GC垃圾回收器帮我们调用。
②只有当对象被确定为垃圾时,才会调用。
③绝大多数情况下子类不需要重写这个方法,只有你这个类型的对象涉及到系统资源的配置与释放的时候才需要重写,
例如:底层的IO流,数据库连接资源,网络的连接资源类型的对象等。
④每一个对象的finalize方法它只会被调用一次。如果在finalize方法中,让当前要把回收的对象this复活了,即又让一个有用的变量引用了当前对象,
当前就不能被回收了。那么当这个对象下次再被丢弃称为垃圾之后,就不会再调用它的finalize()方法,而是直接回收。
面试题:final,finalize,finally的区别
final是修饰符…
finalize是Object类的方法,…
finally是和异常有关
public class ObjectMethod {
public static void main(String[] args) {
TestObjectMethod t = new TestObjectMethod();
System.out.println(t.toString());
//com.atguigu.test02.TestObjectMethod@4554617c
System.out.println(t);
//com.atguigu.test02.TestObjectMethod@4554617c
Student stu = new Student("张三",89);
System.out.println(stu);//默认会调用stu.toString()
//Student{name='张三', score=89}
String str = "学生的信息:";
str = str + stu;//str = str + stu.toString();
System.out.println(str);
/*
编译时类型:obj1和obj2都是Object类型
*/
Object obj1 = "hello";
Object obj2 = 1;//Object的变量只能存储对象
System.out.println(obj1.getClass());//java.lang.String
System.out.println(obj2.getClass());//java.lang.Integer
//p变量的编译时类型是Person,运行时类型是Man
Person p = new Man();
System.out.println(p.getClass());
}
}
class Student{
private String name;
private int score;
public Student() {
}
public Student(String name, int score) {
this.name = name;
this.score = score;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getScore() {
return score;
}
public void setScore(int score) {
this.score = score;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", score=" + score +
'}';
}
}
class Person{
}
class Man extends Person{
}
支持此方法是为了提高哈希表的性能。
==>当我们把对象存储到哈希表的数据结构/容器中时,hashCode是用来提高效率用的。
如果我们不把对象存储到这个中结构中,那么hashCode就没有什么太多用处。
实际上,由 Object 类定义的 hashCode 方法(即默认的hashCode方法)确实会针对不同的对象返回不同的整数。
因为有些JVM实现时确实是将该对象的内部地址转换成一个整数来实现。但是不是 所有的JVM都这么干。
JVM有很多种产品,哪怕是Oracle公司自己也演化过很多版本的JVM。
==>希望大家 “不要” 把hashCode值 = 内存地址。
子类很多时候会重写hashCode方法。重写有要求,需要遵循常规协议:
①只要参与hashCode计算的对象的成员变量的值没有修改,那么在整个程序运行期间无论什么时候,在哪里调用,返回的hashCode值应该是一致的。
②如果两个对象调用equals方法返回true,那么这两个对象的hashCode值必须相同。
③如果两个对象调用equals方法返回false,那么这两个对象的hashCode值可能相同也可能不同。
理想状态下:为不相等的对象生成不同整数结果可以提高哈希表的性能。
==>结论:
①生成hashCode值,不应使用随机值,和系统时间有关这样的因素
因为虽然使用系统时间,可以保证不同的对象,生成不同hashCode值,但是会导致不同的时间同一个对象返回的hashCode会不同。
②重写equals和hashCode一定是一起,不要分开处理。
要么一起重写,要么都不重写,而且选择的成员要一致。
说明;this是调用equals方法的当前对象,o是equals()中的实参对象。
如果我们不使用IDEA或eclipse等开发工具自动生成的equals方法,那么手动重写equals需要遵循如下要求:
①自反性:x.equals(x)必须是true
②对称性:如果x.equals(y)返回true,那么y.equals(x)也需要返回true
③传递性:如果x.equals(y)返回true,y.equals(z)返回true,那么x.equals(z)也要返回true
④一致性:如果参与equals比较的成员变量的值没有修改,那么在整个程序运行期间,两次调用equals的结果应该相同
⑤非空对象与null比较一定是false
如果我们没有重写equlas(),那么Object默认的equals方法实现等价于==。
public class ObjectMethod2 {
public static void main(String[] args) {
Employee e1 = new Employee(1,"张三");
Employee e2 = new Employee(1,"张三");
System.out.println(e1.equals(e2));//如果重写了equals方法返回true,如果没有重写,那么等价于==
System.out.println(e1.hashCode());
System.out.println(e2.hashCode());
String s1 = "Aa";
String s2 = "BB";
System.out.println(s1.equals(s2));//false
System.out.println(s1.hashCode());//2112
System.out.println(s2.hashCode());//2112
String s3 = "Hello";
String s4 = "world";
System.out.println(s3.equals(s4));
System.out.println(s3.hashCode());
System.out.println(s4.hashCode());
System.out.println("-------------------");
System.out.println(s1 == s2);//false ==比较的是两个对象内存地址
System.out.println(e1.equals(e1));//自己与自己比较 true
System.out.println(e1 == e1);//true
System.out.println(e1.equals(null));//当前对象e1非空,o对象是null false
System.out.println(e1.equals("hello"));//e1的getClass是Employee类型,"hello".getClass()是String类型
}
}
class Employee{
private int id;
private String name;
public Employee() {
}
public Employee(int id, String name) {
this.id = id;
this.name = name;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "Employee{" +
"id=" + id +
", name='" + name + '\'' +
'}';
}
枚举是一种特殊的类,这种类特殊在于,它的对象是固定的有限的几个。
普通的类,你想new几个对象就new几个对象。
例如:我们声明星期类型,它的对象是固定的有限的7个。
JDK1.5之前,如何处理?
(1)将这个类型的构造器私有化
(2)在这个枚举类中,提前new好固定的有限的几个对象供外界使用
使用public static 枚举类型 对象名 = new 枚举类型名();
JDK1.5之后,如何处理?
使用关键字enum来声明枚举类型即可。
语法格式:
【修饰符】 enum 枚举类名{
常量对象列表
}
要求:(1)常量对象列表必须在枚举类型的首行
(2)常量对象列表后面如果没有其他的代码,;可以省略,如果有其他代码,必须加;
(3)枚举类型的构造器,默认就是私有化的,如果你要手动编写构造器,也必须是private,
就算你省略了private,也是私有的
(4)建议,如果枚举类中有其他成员变量,建议是final修饰的,即枚举中都是常量对象。
(5)用enum声明的枚举类型,不能自己指定父类,它有固定的直接父类,java.lang.Enum类
class与Class:class声明类,Class是一种类型
enum与Enum:enum声明枚举,Enum是一种类型,
(1)package在.java源文件首行
(2)super()或super(实参列表)在子类构造器的首行
(3)this()或this(实参列表)在本类构造器的首行
(4)枚举常量对象列表,在枚举类型的首行
public class Enum {
public static void main(String[] args) {
/* Week[] weeks = new Week[100];
for (int i = 0; i < weeks.length; i++) {
weeks[i] = new Week();//因为Week的构造器私有化之后,就无法new了
}*/
// Week w1 = new Week();//因为Week的构造器私有化之后,就无法new了
//获取monday对象
Week monday = Week.MONDAY;
// XingQi x = new XingQi();
}
}
class Week{
//public:表示任意位置可见
//static:外界访问时不需要Week对象,使用Week.就可以得到
//final:表示这个对象不可变量,常量对象
//转大小写的快捷键是Ctrl + Shift + U
public static final Week MONDAY = new Week();
public static final Week TUESDAY = new Week();
public static final Week WEDNESDAY = new Week();
public static final Week THURSDAY = new Week();
public static final Week FRIDAY = new Week();
public static final Week SATURDAY = new Week();
public static final Week SUNDAY = new Week();
private Week(){
}
}
enum XingQi{
//表示调用有参构造创建的枚举常量对象
MONDAY("星期一"),
TUESDAY("星期二"),
WEDNESDAY("星期三"),
THURSDAY("星期四"),
FRIDAY("星期五"),
SATURDAY("星期六"),
SUNDAY("星期天");
private final String description;
// XingQi(){//构造器就算没有写private,也是private
//
// }
XingQi(String description) {
this.description = description;
}
}
(1)Enum是一个抽象类,无法直接创建它的对象。
所有用enum声明的枚举类型,就直接继承了Enum类。
(2)单独的构造方法/构造器:
protected Enum(String name, int ordinal)
子类的构造器的首行一定会有super()或super(实参列表)来调用父类的构造器。
默认情况下是使用super()调用父类的无参构造。
在子类的构造器的首行,既不能使用super()来调用父类的无参构造(因为父类没有),
也不能使用super(实参列表)来调用父类的有参构造(因为它已默认帮我们写了,不能再重复编写了)
(3)常用方法:
①String name():返回枚举的常量对象的名称
②int ordinal():返回枚举的常量对象的序号/下标
③boolean equals(Object o):比较两个枚举对象是否相等
④枚举类型 valueOf(枚举常量对象的名称)
⑤枚举[] values()
(4)关于switch从JDK1.5之后增加了对枚举的支持
public class Enum2 {
public static void main(String[] args) {
//获取枚举类型Season的spring常量对象
Season spring = Season.SPRING;
System.out.println(spring.name());
System.out.println(spring.ordinal());
Season spring2 = Season.SPRING;
System.out.println(spring.equals(spring2));//仍然建议对象的比较都用equals
System.out.println(spring == spring2);
Season s = Season.valueOf("SPRING");
System.out.println(spring == s);
Season[] values = Season.values();
for (int i = 0; i < values.length; i++) {
System.out.println(values[i]);
}
Scanner input = new Scanner(System.in);
System.out.print("请输入季节的常量名:");
String name = input.next();
Season season = Season.valueOf(name);
switch (season){
case SPRING:
System.out.println("春天最美丽");
break;
case SUMMER:
System.out.println("夏天最热");
break;
case FALL:
System.out.println("秋天最萧瑟");
break;
case WINTER:
System.out.println("冬天最冷");
break;
}
}
}
enum Season{
SPRING,SUMMER,FALL,WINTER;
}
包装类对象是经常用到的,特别是-128~127范围内的数字。
因此,Java考虑如果每次都new新的对象,就会造成大量的内存的浪费。
所以,Java考虑把这个高频范围的数字的包装类对象给缓存起来,可以共享。
Byte:-128~127
Short:-128~127
Integer:-128~127
Long:-128~127
Float/Double:不缓存
Character:0~127 正好是最早的ASCII表的128个字符。
Boolean:true/false
public class Cache {
public static void main(String[] args) {
Integer i1 = 127;//只有自动装箱时,在缓存范围内的才会使用缓存对象
Integer i2 = 127;
System.out.println(i1 == i2);
Integer i3 = 129;//重新new了Integer对象
Integer i4 = 129;//重新new了Integer对象
System.out.println(i3 == i4);
Integer i5 = new Integer(127);//手动new,不是使用缓存对象
Integer i6 = new Integer(127);//手动new,不是使用缓存对象
System.out.println(i5 == i6);
}
}
知识点:方法的参数传递机制
(1)形参是基本数据类型,实参给形参的是数据值,形参的修改不影响实参
(2)形参是引用数据类型,实参给形参的是地址值,通过形参修改了堆中的成员变量的值,会影响实参对象
当形参如果指向了新对象时,修改形参就和实参无关。
特殊的类型:包装类、String等不可变对象类型。
public class Param {
public static void main(String[] args) {
int a = 1;
Integer b = 1;
MyData c = new MyData();
change(a,b,c);
System.out.println("a = " + a);//1
System.out.println("b = " + b);//1
System.out.println("c.x = " + c.x);//2
}
public static void change(int i, Integer integer, MyData my){
i++;
// integer++;
integer = 2;
my.x++;
}
}
class MyData{
int x = 1;
}
Java语言是一种面向对象的编程语言,所以Java中很多API,或一些特性都是针对对象而设计的,
例如:集合,泛型等
Java因为历史和一些特殊考虑的原因,它保留了C语言的8种基本数据类型和void,这些类型不是
引用数据类型,即不是对象,当程序中,某些数据使用了基本数据类型表示的时候,就无法使用
专门为引用数据类型设计的API和特性了。
基本数据类型的好处:简单,计算速度快,每一种数据类型都有固定大小的存储空间,而且支持很多中运算符。
为了解决这样的矛盾的情况,Java专门为8种基本数据类型和void,设计了对应的包装类。
基本数据类型 包装类(java.lang包)
byte Byte
short Short
int Integer
long Long
float Float
double Double
char Character
boolean Boolean
void Void
(1)装箱:把基本数据类型的值->包装类的对象
JDK1.5之前,需要手动的装箱:
A:构造器 new Integer(基本数据类型)
B:valueOf(基本数据类型)
JDK1.5之后,可以自动装箱:但是它只支持 对应的基本数据类型与包装类之间自动装箱
(2)拆箱:把包装类的对象->基本数据类型的值
JDK1.5之前,需要手动的拆箱:
A:例如:intValue()
JDK1.5之后,可以实现自动的拆箱:但是它只支持 对应的基本数据类型与包装类之间自动装箱
数据类型的转换:
(1)基本数据类型之间:自动类型转换与强制类型转换
(2)父子类之间:向上转型与向下转型
(3)基本数据类型与包装类之间:装箱与拆箱
(1)Integer等
MAX_VALUE
MIN_VALUE
(2)转进制
toBinaryString:二进制
toOctalString:八进制
toHexString:十六进制
(3)转大小写
A:(char)(小写字母-32)得到大写字母
(char)(大写字母+32)得到小写字母
B:Character.toUpperCase(小写字母)得到大写字母
Character.toLowerCase(大写字母)得到小写字母
(4)基本数据类型与字符串之间的类型转换
A:基本数据类型->字符串类型
String str1 = a + “”;//可以使用拼接
String str2 = String.valueOf(a);//推荐
String str3 = Integer.toString(a);//推荐
B:字符串类型->基本数据类型
int i = Integer.parseInt(str);
Integer integer = Integer.valueOf(str);
String string = "1.45";
double v = Double.parseDouble(string);
Double d = Double.valueOf(string);
public class Wrapper {
public static void main(String[] args) {
//JDK1.5之前,需要手动的装箱:
int num = 10;
Integer integer = new Integer(num);
Integer integer1 = Integer.valueOf(num);
//JDK1.5之后,可以自动装箱:
//左边是引用数据类型,右边是基本数据类型
Integer integer2 = num;
//错误:左边是Double包装类,右边1是int,不支持Double与int之间的自动装箱
// Double d = 1;
//可以:左边是Double包装类,右边是double,可以支持自动装箱
Double d = 1.0;
byte b = 1;
//错误:左边是Integer,右边是byte,它们不是对应的类型
// Integer integer3 = b;
Integer integer3 = new Integer(10);
//手动拆箱
int value = integer3.intValue();
//自动拆箱
//左边是基本数据类型,右边是包装类对象
int value2 = integer3;
}
}
(1)自己new
(2)直接获取常量对象
(3)通过调用方法获取某个类型的对象
得到对象后,就可以通过对象.成员变量或对象.成员方法
public class CreateObject {
public static void main(String[] args) {
//创建Demo1的对象
//现new的==>好比去饭店现炒点现炒的
Demo1 d1 = new Demo1();
//获取Demo2的对象
//之前已经new好,我们只是拿到它==>买包装好的方便饭
Demo2 d2 = Demo2.INSTANCE;
//获取Demo3对象
//通过特定的方式获取对象,这个对象可能也是现new,也可能是之前new好
Demo3 d3 = Demo3.getInstance();
Demo4 d4 = Demo4.getInstance();
}
}
class Demo1{
}
enum Demo2{
INSTANCE;
}
class Demo3{
private Demo3(){
}
public static Demo3 getInstance(){
return new Demo3();
}
}
class Demo4{
private static Demo4 instance = new Demo4();
private Demo4(){
}
public static Demo4 getInstance(){
return instance;
}
}
接口是标准,规范。
接口规范中包含两样东西:
(1)底线,边界值:常量
例如:USB1.0的最大传输率:1.5Mbps(192KB/s),电压电流:5V/500mA
(2)行为规范:抽象方法
例如:USB设备中如何“读”和“写”,USE接口的规范中不提出具体的实现步骤,只是说规范,
你应该通过两个功能:一个读和一个写。
很多公司制定了USB标准,但是具体实现在规范中没有提到,
具体的实现由各个厂商来实现这个标准。所有的厂商会提供这个标准的具体实现的驱动程序。
Java中:
例如:声明一个方法,这个方法可以为任意对象数组进行从小到大排序。
发现编写这个排序方法sort()时,遇到了瓶颈,无法提前预知
(1)对象的实际类型
(2)按照对象的xx进行比较大小
这个时候,我们只能提供接口。
例如我们可以指定一种标准MyComparator类型。凡是可以比较大小的类型,都要提供一个方法,如下:
public int compare(Object o1, Object o2)
这个方法的形参:(Object o1, Object o2),表示可以接收两个对象
这个方法的返回值类型是:int
规定:如果o1 大于 o2的话,返回正整数
规定:如果o1 小于 o2的话,返回负整数
规定:如果o1 等于 o2的话,返回零
具体使用时,再来实现接口。
public class Interface {
public static void main(String[] args) {
Circle[] arr = new Circle[3];
arr[0] = new Circle(1.5);
arr[1] = new Circle(1.0);
arr[2] = new Circle(2.5);
CircleComparator my = new CircleComparator();
sort(arr, my);
for (int i = 0; i < arr.length; i++) {
System.out.println(arr[i]);
}
}
public static void sort(Object[] arr, MyComparator my){
for (int i = 1; i < arr.length; i++) {
for (int j=0; j<arr.length-i; j++){
//如果前面的元素 > 后面的元素,就交换
//发现条件无法编写,因为两个对象无法直接比较大小,
//因为arr[j]与arr[j+1]中存的是对象的地址,地址无所谓大小
//如果要比较两个对象的大小,只能根据它们的某个成员变量的值来比较大小
//我在写这个方法的时候,
// 我(1)不知道对象的实际类型
//(2)也不知道这个对象应该按什么比较大小,是按照年龄,还是成绩,还是价格...
/*if(arr[j] > arr[j+1]){
Object temp = arr[j];
arr[j] = arr[j+1];
arr[j+1] = temp;
}*/
//arr[j]大于arr[j+1]的话,my.compare(arr[j],arr[j+1])就会返回一个正整数
//arr[j]小于arr[j+1]的话,my.compare(arr[j],arr[j+1])就会返回一个负整数
//arr[j]等于arr[j+1]的话,my.compare(arr[j],arr[j+1])就会返回一个0
if(my.compare(arr[j],arr[j+1]) > 0){
Object temp = arr[j];
arr[j] = arr[j+1];
arr[j+1] = temp;
}
}
}
}
}
//接口是标准
interface MyComparator{
public abstract int compare(Object o1, Object o2);//抽象方法
}
//接口的实现类,相当于驱动
class CircleComparator implements MyComparator{
@Override
public int compare(Object o1, Object o2) {
Circle c1 = (Circle) o1;
Circle c2 = (Circle) o2;
if(c1.getRadius() > c2.getRadius()){
return 1;
}else if(c1.getRadius() < c2.getRadius()){
return -1;
}
return 0;
}
}
class Circle{
private double radius;
public Circle(double radius) {
this.radius = radius;
}
public double getRadius() {
return radius;
}
public void setRadius(double radius) {
this.radius = radius;
}
@Override
public String toString() {
return "Circle{" +
"radius=" + radius +
'}';
}
}