String、基本数据类型包装类、集合和泛型

时间:2021-02-28 17:27:56


String类:

· 字符串是一个特殊的对象,在java中只要被双引号引起来的都是字符串对象

· 字符串一旦初始化就不可以被改变

· String类复写了Object类中的equals方法,该用法用于判断字符串是否相同

· String s1 = "abc" 和 String s2 = new String("abc");有什么区别?

答:s1在内存中只有1个对象。而s2在内存中有2个对象

· String s1 = "abc",String s2 = new String("abc");String s3 = "abc";这些字符串用==连接结果是什么?

答:s1==s2结果为false,因为s1和s2不是同一个对象。

s1==s3结果为true,因为字符串作为一个特殊的对象,已经在内存中存在了,就不会创建新的对象。所以s1和s3指向的是同一个对象

字符串的常见操作:

获取:

1. 字符串中包含的字符数,也就是字符串长度: int length()

2. 根据位置获取位置上某个字符 char charAt(int index); 当访问到不存在的脚标时,会发生字符串角标越界异常

3. 根据字符获取该字符在字符串中的位置:

int indexOf(int ch) : 返回的是ch在字符串中第一次出现的位置
int indexOf(int ch, int fromIndex) : 从fromIndex指定位置开始,获取ch在字符串中出现的位置

int indexOf(String str) : 返回的是str在字符串中第一次出现的位置
int indexOf(String str, int fromIndex) : 从fromIndex指定位置开始,获取str在字符串中出现的位置

如果没有找到,返回-1

还提供了lastIndexOf的方法,反向索引

判断:

//字符串中是否包含某一个字串
boolean contains(CharSequence str);//CharSequence是一个接口,String是其子类
//字符串中是否有内容
boolean isEmpty();
//字符串是否是以指定内容开头
boolean startsWith(String str);
//字符串是否是以指定内容结尾
boolean endsWith(String str);
//判断字符串内容是否相同。复写了Object类中的equals方法
boolean equals(String str);
//判断字符串内容是否相同,并且忽略大小写
boolean equalsIgnoreCase(String str);

转换:

//将字符数组转成字符串:
//通过String的构造函数:	
String(char[])
String(charr[],offset,count):将字符数组中第一部分转换成字符串
//静态方法:
static String copyValueOf(char[])
static String copyValueOf(char[] data, int offset, int count 

//将字符串转成字符数组 **
char[] toCharArray()

//将字节数组转成字符串和字符数组转成字符串一样,也是构造方法

//将字符串转成字节数组
byte getBytes()

//将基本数据类型转换成字符串
static String valueOf(int):int可以是double等等

特殊:字符串和字节数组在转换过程中,是可以指定编码表的

替换:

String replace(char oldChar,char new Char);//如果要替换的字符串不存在,返回的还是原串

切割:

String[] split(rString egex);

子串,获取字符串的一部分:

String substring(int begIndex):从指定位置开始到结尾

String substring(int begIndex, int endIndex):包含头不包含尾

转换,去除空格,比较:

String toUpperCase()//将字符串转换转成大写
String toLowerCase()//将字符串转换转成小写

String trim()//将字符串两端的多个空格去除
 
int compareTo(String);//将两个字符串进行自然顺序的比较。大于返回的是正数,等于返回的是0,小于返回的是负数

StringBuffer:

· StringBuilder:和StringBuffer一样,只是线程不同步。并且StringBuilder是JDK1.5之后才有的

· StringBuffer是字符串缓冲区:

1. 长度是可变的

2. 可以直接操作多个数据类型

3. 最终会通过toString方法变成字符串

· StringBuffer是一个容器:CURD(create,update,read,delete)

1. 增 create:

StringBuffer append(参数):将指定数据作为参数添加到已有数据的结尾处
StringBuffer insert(int offset,参数):将制定数据作为参数添加到指定位置

2. 删 delete:

StringBuffer delete(int start,int end):删除缓冲区的数据,包含start,不包含end
StringBuffer deleteCharAt(int index):删除指定位置的字符

3. 读 read:

char charAt(int index)
int indexOf(String str)
int lastIndexOf(String str)
int length()
String substring(int start,int end)

4. 改 update:

StringBuffer replace(int start,int end,String str):替换一段位置内的字符串
void setCharAt(int index,char c):替换指定位置的字符
StringBuffer reverse():反转
void getChars(int srcBegin,int srcEnd,char[] dst,int dstBegin):将缓冲区中的指定

· 字符串的组成原理就是通过该类实现的,很多方法和String相同

基本数据类型对象包装类:

· 将基本数据类型封装成对象的好处在于可以在对象中定义更多的功能方法操作数据

· 对应关系中,都是将首字母大写的就是对应的包装类,除了int对应Integer和char对应Character

· 常用操作之一:用于基本数据类型与字符串之间的转换

例:Integer.parseInt("43");//将字符串转成整数

例:在Integer中,就可以直接获取整数类型的最大值,Integer.MAX_VALUE

· 新特性:在JDK1.5之后,简化了定义的方法:

例:Integer x = new Integer(4) 可以直接写成 Integer x = 4;//自动装箱。x = x + 5;//自动拆箱,x变成了int类型,通过intValue()

· 需要注意:

1. 在使用时,Integer x = null,上面的代码就会出现NullPointException

2. 当数值在byte范围内,对于新特性,如果该值已经存在,则不会开辟新的内存空间。所以Integer a = 128 和 Integer b = 128。进行==判断的结果为false

集合:

· 为什么要出现集合?

答:面向对象语言对事物的体现都是以对象的形式,所以为了方便多个对象的操作,就对对象进行存储,集合就是就是存储对象最常用的一个方法。

· 数组和集合类同是容器,有何不同?

答:数组虽然也可以存储对象,但是长度是固定的;集合长度是可变的。数组中可以存储基本数据类型,集合只能存储对象

· 集合的特点:

答:集合只用于存储对象,集合长度是可变的,集合可以存储不同类型的对象

集合框架的构成以及分类:

String、基本数据类型包装类、集合和泛型

· 为什么要有这么多容器?

答:因为每一个容器对数据的存储方式都不同,这个存储称之为:数据结构

集合框架中的常用接口:

· Collection接口中有2个子接口:List(列表),Set(集)

· Collection中定义的常用方法:

1. 增:add(Object obj)

2. 删:remove(Object obj)

3. 获取,通过迭代器:Iterator iter = collection.iterator()

· List:可存放重复元素,元素存取是有序的。因为该集合体系有索引。凡是可以操作脚标的方法,都是该体系特有的方法

1. 增:add(index, element):指定脚标添加元素;addAll(index, Collection):指定角标添加一堆元素 

2. 删:remove(index)

3. 改:set(index, element)

4.查:get(Index) subList(from) ListIterator():Iterator的子接口,比Iterator多了添加和修改的方法

· Set:不可以存放重复元素,元素存取是无序的。该集合体系没有索引

· 注意:集合中存储的都是对象的引用

List接口中常用类:

· Victor:线程安全,但速度慢,已经被ArrayList取代。Vector特有方法,elements()放回的一个枚举Enumeration。Enumeration方法和Iterator一样,但是Enumeration方法和方法名都过长,所以Iterator取代了

· ArrayList:线程不安全,查询速度快。底层使用的是数据结构

· LinkedList:链表结构,增删速度快。

迭代:

· 迭代是取出集合中元素的一种方法

· 因为Collection中有iterator方法,所以每一个子类集合对象都具备迭代器

· 用法:

//for循环方法
for(Iterator iterator = collection.iterator();iter.hasNext(); ) 	{
	iterator.next();//返回值类型是Object
}
//while循环方式
Iterator iterator = collection.iterator();//获取迭代其
while(iterator.hasNext()) {
	iterator.next();
}

迭代器注意事项:

· 迭代器在Collection接口中是通用的,它替代了Vector类总的Enumeration

· 迭代器的next方法是自动向下取元素,要避免出现NoSuchElementException

· 迭代器的next方法返回值类型是Object,所以记得要做强制类型转换

Set接口中常用的类:

· HashSet:线程不安全,存取速度快

· TreeSet:线程不安全,可以对Set集合中的元素进行排序

Set集合元素唯一性原因:

· HashSet:通过equals方法和hashCode方法来保证元素的唯一性。判断元素的hashCode值是否相同,如果相同,还会继续判断元素的equals方法,是否为true

· TreeSet:通过compareTo或者compare方法来保证元素的唯一性。元素是以二叉树形式存在的。

1. TreeSet排序的第1种方法,让元素自身具备比较性,元素实现Comparable接口,覆盖compareTo方法。这种方法也称为元素的自然顺序,或者叫默认顺序

2. TreeSet的第2种排序方式,当元素自身不具备比较性时,或者具备的比较性不是所需要的。这时就要让集合自身具备比较性。在集合初始化时就有了比较方式,定义了比较器,将比较器作为参数传递给TreeSet集合构造函数。

· 当2种排序都存在时,以比较器为主。

· 定义比较器的方法:定义一个类,实现Comparator接口,覆盖compare方法。

泛型:

· JDK1.5以后出现的新特性。用于解决安全问题,是一个类型安全机制

· 好处:

1. 将运行时期出现的问题ClassCastException,转移到了编译时期。方便与程序员解决问题。让运行时问题减少,安全

2. 避免了强制类型转换的麻烦

· 在使用java提供的对象时,什么时候写泛型?

答:通常在集合框架中很常见,只要见到<>就要定义泛型。其实<>就是用来接收类型的。当使用集合时,将集合中要存储的数据类型作为参数传递到<>中即可。

· 缺点:不能预先使用子类中特有的方法

自定义泛型:

· 代码示例:

class Utils<E> {
	private E e;
	public void setObject(E e) {
		this.e = e;
	}
	public E getObject() {
		return e;
	}
}
· 自定义泛型方法,泛型类定义的泛型,在整个类中有效,如果被方法调用,那么泛型类的对象明确要操作的具体类型后,所有要操作的类型就已经固定了。
//为了然不同的方法可以操作不同的类型,而且类型还不确定。那么可以将方法定义在泛型上!
public <T> void show(T t) {
	System.out.println("show"+t);
}
//特殊之处:静态方法不可以访问类上定义的泛型,如果静态方法操作的应用数据类型不确定,可以将泛型定义在方法上。
public static <E> void print(E e) {
	System.out.println("print"+e);
}

· 什么时候定义泛型?

答:当类中要操作的引用数据类型不确定,早起定义Object类扩展,现在定义泛型

泛型的特点:

· 提高了程序的安全性

· 将运行期间遇到的问题转移到了编译时期

· 省去了类型转换的麻烦

· 泛型类的出现优化了程序设计

泛型限定:

· ? 通配符,也可以理解为占位符。

· ? extends E:可以接收E类型或者E类型的子类。称为上限

· ? super E:可以接收E类型或者E类型的父类。称为上限

所以:在java设计的时候,就用到了泛型的限定。例如Collection就可以接收类和类的子类。

Map集合

· Set集合底层就是使用了Map集合

· Map集合存储与取出元素的方法

//1. 添加
V put(K key, V value);//当你存了相同的键的时候,新的value换替换原来的value。并且会返回原来的value
V putAll(Map<? extends K,? extends V> n)
//2. 删除
void clear():移除所有
V remove(Object key)
//3. 判断
boolean containsKey(Object key)
boolean cotainsValue(Object value)
boolean isEmpty();
//4. 获取
V get(Object key)
int size()
Collection<V> values()
Set<Map,Entry<K,Y>> entrySet()
Set<K> keySet()

· 什么时候用Map集合呢?

答:当数据之间存在着映射关系时,就要先想到map集合

Map与Collection:

· Map与Collection在集合框架中并列存在

· Map存储的是键值对

· Map存储元素使用put方法,Collection使用add方法

· Map集合没有直接取出元素的方法,而是先转成Set集合,再通过迭代取出

· Map集合要保证元素的唯一性

Map集合的2种取出方式:

//第1种:Set<K> keySet:将map中所有的键存入到Set集合,因为set具备迭代器,可以用迭代方式取出所有的键,再根据get方法。获取每一个键对应的值
Map<Integer,String> map = new HashMap<Integer, String>();
map.put(1, "张飞");
map.put(2, "关羽");
map.put(3, "刘备");
map.put(4, "赵云");
//先获取map集合中键的Set集合
Set<Integer> keySet = map.keySet();
//有了Set集合,就可以获取其迭代器了
Iterator<Integer> iterator = keySet.iterator();
while(iterator.hasNext()) {
	int key = iterator.next();
	String value = map.get(key);
	System.out.println("key="+key+",value="+value);
}

//第2种:Set<Map.Entry<K,V>> entrySet:将Map集合中的映射关系存入到了set集合中,而这个关系数据类型就是:Map.Entry
Map<Integer,String> map = new HashMap<Integer, String>();
map.put(1, "张飞");
map.put(2, "关羽");
map.put(3, "刘备");
map.put(4, "赵云");
//将Map集合中的映射关系取出,存入到集合中
Set<Map.Entry<Integer,String>> entrySet = map.entrySet();
Iterator<Map.Entry<Integer,String>> iterator = entrySet.iterator();
while(iterator.hasNext()) {
	//每个映射关系就是一个Map.Entry
	Map.Entry<Integer,String> entry = iterator.next();
	int key = entry.getKey();
	String value = entry.getValue();
	System.out.println("key="+key+",value="+value);
}

· Map.Entry:其实Entry也是一个接口,是Map接口中的一个static interface。因为Entry存放的是映射关系,所以要先有Map才能有Entry,所以Entry作为Map的内部类。

Map集合常用类:

· Hashtable:线程安全,速度慢。不允许存放null键、null值,已被HashMap替代

· HashMap:线程不安全,速度快。允许存放null键,null值

· TreeMap:线程不安全,对键进行排序,排序原理与TreeSet相同

Map集合的扩展知识:

· Map集合中还可以嵌套Map集合。也就是存在一对多的关系的时候,虽然可以使用多个Map,但是在取出的时候需要多个嵌套循环。所以开发一般把对象封装起来。

集合框架中的工具类:

· Collections就是对Collection集合提供的一些工具方法

· 常用方法:

//1,使用二分搜索法搜索指定列表,获取指定对象。返回插入点-1
static<T> int binarySearch(List<? extends Comparable<? super T>> list, T key)
static<T> int binarySearch(List<? extends Comparable<? super T> list, T key, Comparator<? super T> c)
//2,反转指定列表中元素的顺序
static reverse(List<?> list)
//3,强行逆转一个比较器或者一个元素的自然顺序
static<T> Comparator<T> reverseOrder()
static<T> Comparator <T> reverseOrder(Comparator<T> cmp)
//4,使用默认随机源对指定列表进行置换,也可以使用指定随机源
static void shuffle(List<?> list)
static void shuffle(List<?> list,Random rnd)
//5,根据元素的自然顺序对指定列表进行排序,也可以指定一个比较器
static <T extends Comparable<? super T>> void sort(List<T>  list)
static<T> void sort(List<T> list,Comparator<? super T> c)
//6,在指定列表的指定位置出交换元素
static void swap(List<?> list,int i,int j)
· Collection和Collections有什么区别?

答:Collection是集合框架的一个顶层接口,它里面定义了单列集合的共性方法。它有2个常用子类接口(List和Set)。而Collections是集合框架的一个工具类,该类中的方法都是静态的。提供的方法中有可以对List集合进行排序,二分查找等。通常常用的集合都是线程不安全的,因为要提高效率。如果多线程操作这些集合时,可以通过该工具类中的方法,将线程不安全的集合,转换成安全的。

· Arrays:用于操作数组的工具类。大部分方法与Collections一致

//1. 数组变集合:
static <T> list <T> asList(T... a) 返回一个受指定数组支持的固定大小的列表
/*
把数组变集合有什么好处?
可以使用集合的思想和方法来操作数组中的元素。
注意:
(a)将数组变成集合,不可以使用集合的增删方法。因为数组的长度是固定的。如果使用了增删方法,会发生UnsupportedOperationException。
(b)如果数组中的元素都是对象,那么变成集合时,数组中的元素就直接转成集合中的元素。
(c)如果数组中的元素是基本数据类型,那么会将该数组作为集合中的元素存在。例如:
int[] arr = {3,7,1};
					List<int[]> list = Arrays.asList(arr);
	System.out.println(list);//这时输出值是数组的地址值
*/
//2. 集合变数组:
//Collection接口中的toArray方法:
Object[] toArray()
<T> T[] toArray(T[] a)
/*
1,指定类型的数组到底要定义多长呢?
当指定类型的数组长度小于了集合的size,那么该方法内部就会创建一个新的数组。长度为集合的size。
当指定类型数组的长度大于了集合的size,就不会创建新的数组。而是使用传递进来的数组。
所以创建一个刚刚好的数组最优。
2,为什么要将集合变数组?
为了限定对元素的操作。不需要进行增删了。
*/

高级for循环:

· Collection在JDK1.5后出现的父接口,iterator就是提供了这个for语句
· 格式:
for(数据类型 变量名:数组或集合) {
	执行语句;
}

· 简化了对数组,集合的遍历。但是只能获取集合元素,不能对集合进行操作。迭代器除了遍历,还可以进行remove集合中的元素。如果是ListIterator,还可以的在遍历过程中对集合进行增删改查的动作。

· 传统for循环和高级for循环有什么区别?

答:高级for循环有一个局限性,必须有被遍历的目标。所以建立遍历数组时,还是希望使用传统for循环。因为传统for循环可以定义脚标。

· 代码示例:

//对Map集合进行keySet遍历:
for(Map.Entry<K, V> m : map.entrySet()) {
	System.out.println(m.getKey()+","+m.getValue());
}
//对Map集合进行entrySet遍历:
for(K s : map.keySet()){
	System.out.println(s+","+map.get(s));
}
//对数组进行遍历示例:
int[] arr = {1,3,5};
for(int x : arr) {
	System.out.println(x);
}

函数的另外一种表现形式:可变参数

· 格式:
返回值类型 函数名(参数类型... 形式参数) {
	执行语句;
}
· 其实接收的是一个数组,可以指定实际参数个数。这样就不用每一次都手动的建立数组对象,只要将要操作的元素作为参数传递即可。隐式地将这些参数封装进了数组
· 可变参数一定要定义在参数列表的最后