1.包(package)
1.1 包的概念
包的位置:类上面,关键字package
包的真实意义:实际开发中对代码进行分层
一般命名:单词全部小写,而且是多级包,中间打点隔开,点就是分包(目录/文件夹)
1.2 带包的编译和运
手动方式:
1)先将某个.java文件编译
2)在当前目录下创建package后的目录 com.qf--->com文件夹下 qf文件夹
3)将(1)产生的字节码文件放在(2)qf文件夹里面
4)java 包名.类名
自动方式:
1)javac -d . java源文件 ----自动将包名以及包名下的类名.class产生出来
2)java 包名.类名
2.内部类
在一个类class A中嵌套了另外一个类class B,我们就称class B为内部类
2.1 成员内部类
在外部类成员位置(类中方法外)的内部类
class Outer{//外部类
class Inner{//成员内部类
}
}
成员内部类的特点
成员内部类可以直接访问外部类的所有成员(包括私有)
外部类要访问内部类的成员,必须通过内部类的对象来访问
如何直接通过外部类来访问内部类的成员方法?
前提条件:成员内部类是非静态的
外部类名.内部类名 对象名 = new 外部类名().new 内部类名();
Outer.Inner inner = new Outer().new Inner();
成员内部类可以被private修饰,保护数据的安全性
成员内部类可以被static修饰,这个成员内部类的中的所有的方法,无论是静态的还是非静态的,访问外部类的成员必须只能访问静态的东西
外部类直接访问静态内部类的成员,可以将内部类看成外部类的成员
外部类名.内部类名 对象名 = new 外部类名().内部类名();
访问静态内部类的非静态方法:对象名.方法名();
访问静态内部类的静态方法:外部类名.内部类名.方法名();
2.2 局部内部类
在外部类方法中定义的内部类,称为局部内部类
class Outer{//外部类
public void method(){//成员方法
class Inner{//局部内部类
}
}
}
局部内部类的特点
可以直接访问外部类的所有成员(包括私有)
外部类如何直接访问内部类的成员?
在局部位置(方法中)创建内部类的对象,使用对象调用内部类的成员
public class Test{
public static void main(String[] args){
//创建外部类对象
Outer outer = new Outer();
//访问外部类方法
outer.show();
}
}
//外部类
class Outer{
private int i = 10;
//外部类的成员方法
public void show(){
int num = 20;
//局部内部类
class Inner{
//成员方法
public void method(){
//访问外部类的私有成员属性
System,out.println(i);
//访问外部类的局部变量num
System.out.println(num);
}
}
//创建内部类对象
Inner inner = new Inner();
//调用内部类的成员方法
inner.method();
}
}
2.3 匿名内部类
匿名内部类:就是没有名字的内部类
格式:
抽象类或者接口的匿名内部类
new 类名或者接口名{
重写方法(){
}
};
适用场景:在方法的形式参数以及返回值是抽象类或者接口的情况下使用
匿名内部类的本质:就是继承了该类(抽象类)的子类对象或者是实现了该接口的子实现类对象
interface Love{
void love();
}
//外部类
class Outer{
public void method(){
//没有使用匿名内部类之前
/*
class Inner{
public void show(){
System.out.println("show Inner...");
}
}
//创建Inner对象
Inner inner = new Inner();
inner.show();
*/
//使用匿名内部类
Love i = new Love(){
@Override
public void show(){
System.out.println("show Love");
}
};
i.show;
}
}
//测试类
public class Test{
public static void main(String[] args){
//创建外部类对象
Outer outer = new Outer();
outer.method();
}
}
匿名类的使用:形式参数为抽象类
//定义一个抽象类
abstract class Person{
//抽象方法
public abstract void work();
}
class PersonDemo{
//方法形式参数为抽象类
public void method(Person p){
p.work();
}
}
//测试类
public class Test{
public void main(String[] args){
PersonDemo pd = new PersonDemo();
//使用匿名内部类
pd.method(new Person(){
@Override
public void work(){
System.out.println("我爱工作");
}
};)
}
}
匿名类的使用:返回值为接口
//定义一个接口
interface Cry{
void cry();
}
class CryDemo{
public Cry method(){
return new Cry(){
@Override
public void cry(){
System.out.println("小狗汪汪叫");
}
};
}
}
//测试类
public class Test{
public static void main(String[] args){
//创建CryDemo类对象
CryDemo cd = new CryDemo();
//调用方法
Cry c = cd.method();
c.cry();
}
}
3.Java常用类
3.1 Object类
所有类的父类,几乎所有的成员方法都要被任何子类重写
常用功能:
public String toString():本身含义:返回对象的字符串表现形式。
建议所有子类重写,返回的结果应该是一个简明扼要的信息表达式
而不是一堆地址值
public int hashcode():返回对象的哈希码值(可以理解为地址,但是不是
实际意义上的地址),每一个对象的哈希码值都是不同的
public boolean equals(Object o):比较两个对象是否相同,
建议所有子类重写该方法,否则,默认比较引用类型的地址值是否相同重写之后,
比较的是对象的内容是否相同
3.2 String类
特点:
String类:字符串是一个常量,采用创建字符串对象的方式,常量赋值—代表当前字符串的实例,对象一旦被创建,其值(常量池的地址)就不能被改变,除非重新去赋值
String类作为引用类型,当成为方法的形式参数,和基本类型的效果一致,形参的改变,不影响实参
String类的构造方法
String(char[] chs) :将字符数组构造成String
String(byte[] bytes):将字节数组构造成String
String类的常用功能
获取功能:
1)public char charAt(int index):获取指定索引处的字符
2)public String concat(String str):字符串拼接功能
3)public boolean endsWith(String suffix):
判断此字符串是否以指定的字符串结尾
4)public boolean startsWith(String prefix〕:
判断此字符串是否以指定的字符串开头
5)public boolean equals(Object anObject):
比较是两个字符串的内容是否相同
6)public boolean equalsIgnoreCase(String anotherString) :
比较两个字符串内容是否相同,忽略大小写进行比较
7)String subString(int beginIndex):
通过截取字符串,获取一个新的字符串(从指定位置截取,默认截取到末尾)
8)String subString(int beginIndex, int endIndex):
通过截取字符串,获取一个新的字符串,
从指定位置开始,到指定位置(endIndex)结束
9)int length():获取字符串长度
转换功能:
1)char[] toCharArray() :将String转换字符数组
2)byte[] getBytes() :将字符串转换成字节数组
3)String[] spilt(String regex):通过指定的分割符号,将字符串转换成字符串
数组
4)public static String valueOf(int/double/long/....Object):可以将任何类
型转换String
5)String toUpperCase() :转换成大写
6)String toLowerCase():转换成小写
其他功能:
public int compareTo(String str):按照字典顺序进行比较两个字符串
方法解释:
将两个字符串转换为字符数组,获取两个字符数组的长度
Math.min(长度1,长度2)
定义一个统计变量
while(统计变量<最小长度){
统计变量从0开始,依次比较两个字符数组的每一个字符值
判断每一个字符值是否不相等
如果不相等,则返回两个字符的ASCII码值相减的结果
}
循环结束,如果上面判断一直不成立
则返回长度1-长度2
3.3 StringBuffer类
StringBuffer:线程安全的可变的字符串缓冲区
常用构造方法
StringBuffer():空参构造,创建一个空的字符串缓冲区对象
StringBuffer(String str):
将指定的字符串存储在字符串缓冲区,将String类型转换成StringBuffer类型
StringBuffer常用功能:
1)public StringBuffer append(任意类型数据):
添加任意类型数据到字符串缓冲区中,返回字符串缓冲区本身
2)public StringBuffer reverse():
将字符串缓冲区字符序列反转
3)public StringBuffer insert(int offset, Object obj):
添加Object类型的参数到该字符序列中
3.4 String和StringBuffer的区别
String的特点:字符串常量,一旦创建,其值不改变,不支持可变的字符序列
StingBuffer:支持可变的字符序列,而且线程安全的---执行效率低,多线程会用,
单线程环境下用StringBuilder类
方法的形式参数:
String和基本类型效果一样,形参的改变不影响实参
StringBuffer:作为方法的形式参数,形参改变会影响实参
3.5 Integer类
自动拆装箱
基本数据类型—>对应的引用数据类型:“装箱”
引用数据类型—>对应的基本数据类型:“拆箱”
Integer类的构造方法
public Integer(int value):将int类型----Integer类型
public Integer(String s) :将数字字符串转换成Integer
字符串保存的必须是数字字符串
Integer内部缓存区
内部缓存区:Integer类的静态的成员内部类 IntegerCache
low和high的值:-128~127;
如果直接将int类型直接赋值给Integer,实际底层调用的是Integer.valueOf(int的值)
判断当前int类型的值是否在-128~127之间,如果在,直接从内部缓存区中的Integer[] cache中取得数据,并返回该值,如果超过这个范围,则重新创建对象new Integer(int类型的值)
3.6 Character类
构造方法
Character (char value):将一个char类型的值构造成一个Character类对象
常用方法
1)public static boolean isUpperCase(char ch)
判断字符是否为大写字母字符
2)public static boolean isLowerCase(char ch)
判断字符是否为小写字母字符
3)public static boolean isDigit(char ch)
判断字符是否为数字字符
3.7 Date类
表示日期,可以精确到毫秒
构造方法
public Date():无参构造方法:创建日期对象,
获取当前系统时间的日期格式:包含星期 年份 月份 月中的日期。..
public Date(long date):创建日期对象,和1970年1月1日有关系
常用功能
public long getTime():获取时间的毫秒值
Date重点:日期文本格式和Date如何转换?
使用中间桥梁:DateFormat类 是一个抽象类,不能实例化,
jdk提供更具体的子类:SimpleDateFormat
构造方法
public SimpleDateFormat(String pattern)
yyyy:年 MM:月 dd:月中日期 HH:小时 mm:分钟 ss:秒
成员方法:
Date---->String public String foramt(Date date) :格式化
String--->Date: public Date parse(String dateStr) throws ParseException: 解析
这个本身可能出现异常,如果解析的字符串的格式和 pattern参数的模式匹配,就出错了,解析失败!
//需求:键盘录入出生日期,计算来到这个世界多少天
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Scanner;
public class Test {
public static void main(String[] args) throws ParseException {
//获取当前系统时间
Date nowDate = new Date();
//创建键盘录入对象
Scanner sc = new Scanner(System.in);
//提示用户输入并接收
System.out.print("请输入出生年月日(yyyy-MM-dd):");
String bornDate = sc.nextLine();
//将bornDate日期文本转换为Date格式
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
Date birthDay = sdf.parse(bornDate);
//获取出生日期的毫秒值
long oldTime = birthDay.getTime();
//获取当前日期的毫秒值
long newTime = nowDate.getTime();
//输出毫秒值
System.out.println("出生日期的毫秒值为:" + newTime);
System.out.println("当前日期的毫秒值为:" + oldTime);
//计算差值,换算成天数
long day = (newTime - oldTime) / 1000 / 60 / 60 / 24;
//输出结果
System.out.println("你来到这个世界已经" + day + "天了");
}
}
3.8 Random类
随机数生成器
构造方法
Random():创建对象后,调用nextInt()/nextInt(int n):每次随机数不一样
public Random(long seed) :不推荐,调用nextInt()/nextInt(int n):每次随机数一样
成员方法
int nextInt():int类型的范围获取
int nextInt(int n):范围是[0 , n)
3.9 System类
常用功能
1)public static void exit(int status):
参数为0,退出Jvm(正常终止)(如果不为0 ,属于异常终止)
2)public static long currentTimeMillis():获取当前系统时间的毫秒值
3)public static void gc():手动开启垃圾回收器,回收没有更多引用的对象,
会调用Object类的finaliza()方法
4)public static void arraycopy(Object src, 数据源(举例:原数组)
int srcPos, 原数组中某个位置
Object dest, 目的数据(目标数组)
int destPos, 目标数组的某个位置
int length 复制的长度 )
4. 集合
集合是个容器,特点:支持可变长度
4.1 Colleation集合
Collection接口:集合的顶层次的根接口,代表所有的单列集合,只能存储一种引用类型的集合
Collection接口<引用类型>
List子接口 Set子接口
实现类 实现类
ArrayList HashSet
LinkedList TreeSet
Vector
常用方法:
boolean add(E e):在集合末尾添加元素
boolean contains(Object o):判断集合中是否有指定的元素
boolean remove(Object o):删除本集合中的o元素
void clean():清空本集合
boolean isEmpty():判断集合是否为空
int size():获取集合中的元素个数
Object[] toArray():返回一个包含本集合所有元素的数组
Iterator iterator():迭代器,集合专用的遍历方式
集合的专有遍历方式----“迭代器”
Iterator<E> iterator()
返回值是一个接口:
Iterator:
Object next()返回迭代中的下一个元素。 (返回任意java类型)
boolean hasNext():判断功能:判断迭代器中是否下一个元素
返回true,表示存在
返回false,不存在!
4.2 List集合
List集合特点
(1)元素可以重复
(2)存储顺序和取出顺序一致(有序性)
List集合存储字符串,有几种方式遍历?
(1)使用Object[] toArray()方法,将对象转换为数组进行遍历
(2)使用迭代器 Iterator<E> iterator()进行遍历
特有方式:
(1)普通for()循环:使用int size()和E get(int index)方法进行遍历
(2)使用集合的列表迭代器ListIterator listiterator()
ListIterator:
boolean hasNext():是否有下一个元素
E next():获取下一个元素
(3)使用增强for:增强for是jdk5的新特性,为了代替迭代器的使用(简化代码书写)
for(集合中存储的数据类型 变量名:集合对象或者数组对象){
使用变量即可
}
List集合常用的三个子实现类:ArrayList,LinkedList,Vector
三个子实现类的特点:
(1)ArrayList:底层数据结构是数组,查询快,添加删除比较慢;
从线程的角度来考虑,ArrayList是不同步的,不安全的,但是效率比较高
(在单线程程序中,只考虑效率)
(2)Vector:底层数据结构是数组,查询快,添加删除比较慢;
从线程的角度来考虑,Vector是一个同步的,安全的集合,执行效率比较低
如果在多线程的环境下,就必须使用Vector集合,因为要考虑安全性
(3)LinkedList:底层数据结构式链表,查询比较慢,因为每一次查询都需要从链表开头查询;
添加和删除比较方便
从线程的角度考虑,LinkedList是一个线程不安全的集合,不同步,效率比较高
List集合存储自定义对象去重
分析:
(1)首先创建一个List集合对象,往集合中添加自定义类型的对象
(2)创建一个新的List集合
(3)编译以前的集合,获取到每个元素,在新的集合中判断是否有以前集合的元素,如果没有,
就将这个元素添加到新集合中
使用boolean contains(Object o)方法判断
(4)最终遍历新集合
注意:必须在自定义类中重写equals()方法和hashCode()方法,
否则contains方法比较的是对象的地址值,而不是对象的内容
import java.util.ArrayList;
import java.util.List;
public class Exercise {
public static void main(String[] args) {
//创建List集合对象
List<Student> list = new ArrayList<>();
//添加元素
list.add(new Student("张三", 18, '男'));
list.add(new Student("张三", 18, '男'));
list.add(new Student("李四", 17, '男'));
list.add(new Student("小红", 17, '女'));
list.add(new Student("小花", 18, '女'));
list.add(new Student("小花", 16, '女'));
list.add(new Student("小花", 16, '女'));
for (Student s : list){
System.out.println(s);
}
//创建一个新的集合
List<Student> c = new ArrayList<>();
//增强for遍历原集合
for (Student s : list) {
//判断新集合是否有当前元素,如果没有,就将当前元素添加到新集合中
if (!c.contains(s)){
c.add(s);
}
}
System.out.println("--------去重后--------");
for (Student s : c) {
System.out.println(s);
}
}
}
//自定义学生类
class Student{
private String name;
private int age;
private char gender;
public Student(String name, int age, char gender) {
this.name = name;
this.age = age;
this.gender = gender;
}
public String getName() {
return name;
}
public int getAge() {
return age;
}
public char getGender() {
return gender;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
", gender=" + gender +
'}';
}
//重写equals方法
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Student student = (Student) o;
return age == student.age && gender == student.gender && name.equals(student.name);
}
//重写hashCode方法
@Override
public int hashCode() {
return Objects.hash(name, age, gender);
}
}
代码运行结果
4.2.1 LinkedList
特点:底层数据结构是链表,特点:查询慢,添加删除快,是线程不安全的
特有功能
(1)public void addFirst():将指定元素添加到链表开头
(2)public void addLast():将指定元素添加到链表结尾
(3)public E removeFirst():删除第一个元素并返回
(4)public E removeLast():删除最后一个元素并返回
(5)public E getFirst():获取第一个元素
(6)public E getLast():获取最后一个元素
(7)public void push(E e):推送到链表开头
(8)public E pop():弹出最后一个元素
4.2.2 Vector
底层数据结构是数组,特点:查询快,添加删除比较慢
线程安全的类—同步的—执行效率比较低(多线程环境下使用安全性比较大的类)
特有功能:
(1)public void addElement(E obj):
添加元素,如果E的数据类型确定了,就只能添加这一种类型的元素
(2)public Enumeration<E> element():获取组件(返回值是枚举接口---产生一系列元素)
类似与Colleation集合的Iterator iterator()迭代器
Enumeration枚举接口
boolean hasMoreElement():判断是否有更多的元素
---类型与Iterator迭代器里面的hasNext()
E nextElement():获取下个元素
---类似于Iterator迭代器中的next()
(3)public E elementAt(int index):通过索引获取元素
4.3 Set集合
特点:保证元素唯一,而且不能保证迭代次序
唯一,无序性(元素不重复,不能保证顺序—哈希表(key-value键值对)完成的(桶状结构)
为什么会保证元素唯一?HashSet依赖于Map<K,V>接口的子实现类HashMap<K,V>实现的
HashSet的add()方法依赖于HashMap的put方法,间接依赖于Object类的equals和hashCode
特点的代码体现
import java.util.HashSet;
import java.util.Set;
public class HashSetDemo {
public static void main(String[] args) {
//创建HashSet对象
Set<String> set = new HashSet<>();
//添加元素
set.add("java");
set.add("java");
set.add("hello");
set.add("Hello");
set.add("world");
System.out.println(set);
}
}
5. 权限修饰符
本类 | 同包子类,同包其他类 | 不同包子类 | 不同包其他类 | |
---|---|---|---|---|
public | √ | √ | √ | √ |
protected | √ | √ | √ | |
默认 | √ | √ | ||
private | √ |
6.形式参数或者返回值为具体类,抽象类或者接口的问题
6.1 形式参数问题
1. 形参为具体类
方法的形参类型为具体类,那么传递的时候应该传递这个类的一个具体对象
2. 形参为抽象类
方法的形参类型为抽象类,那么传递的时候应该传递这个抽象类的子类对象
3. 形参为接口
方法的形参类型为接口,传递参数的时候应该传递这个接口的子实现类对象
6.2 返回值问题
1. 返回值为具体类
方法的返回值类型为具体类,应该返回当前类的一个具体对象
2. 形参为抽象类
方法的返回值类型为抽象类,应该返回该抽象类的子类对象
3. 形参为接口
方法的返回值类型为接口,应该返回该接口的子实现类对象
7.面试题
7.1 成员内部类面试题
/*
看程序补全代码,在控制台输出30,20,10
*/
public class Test{
public static void main(String[] args) {
Outer.Inner oi = new Outer().new Inner();
oi.method();
}
}
class Outer{//外部类
int num = 10;
class Inner{//成员内部类
int num = 20;
//成员内部类的成员方法
public void method() {
int num = 30;
//在下面补全代码
System.out.println(num);
System.out.println(this.num);
System.out.println(new Outer().num);
}
}
}
7.2 局部内部类面试题
jdk版本7的版本,局部内部类访问的外部类的成员方法的局部变量,这个局
部变量有什么特点?为什么?
jdk7及以前版本:
定义变量:int num = 20;//立即报错,变量需要用final修饰
jdk8已经优化
定义变量:int num = 20;//不会报错
为什么?
因为局部变量的生命周期是
随之方法的调用而存在,随着方法的调用结束而消失,
在代码中,show()方法调用完毕,num就应该被释放,
但是,在show()方法中,创建了一个局部内部类的对象,
对象中的成员方法在间接的使用外部类的局部变量,
所以不能让num立即释放,
所以用final修饰,让num变成了一个常量。
7.3 集合和数组有什么区别
(1) 长度的区别
数组:长度固定
集合:长度可变
(2)存储的数据类型的区别
数组:既可以存储基本数据类型,也可以存储引用数据类型
集合:只能存储引用数据类型
(3)存储元素的区别
数组只能存储同一种类型的元素
集合本质可以存储任意引用类型的元素(多种引用类型)