此博客为看B老师Java视频的部分记录
- 多线程通信生产消费问题
-
概要:生产者生产商品后告诉消费者,此时生产者应该处于等待状态‘消费者消费了商品后,应告诉生产者,此时消费者处于等待状态
-
Condition对象中部分方法:
- await(): 让线程处于等待状态(将线程临时存储到线程池中)
- signal(): 唤醒线程池中任意一个等待的线程
- signalAll(): 唤醒线程池中所有的等待线程
-
部分示例
class BoundedBuffer
{
final Lock lock = new ReentrantLock(); //锁
final Condition notFull = lock.newCondition(); //生产
final Condition notEmpty = lock.newCondition(); //消费
final Object[] items = new Object[100]; //存储商品的容器
int putptr /*生产者使用的角标*/, takeptr/*消费者使用的角标*/, count/*计数器*/;
//生产者使用的方法,往数组中存储商品
public void put(Object x) throws InterruptedException
{
lock.lock();
try
{
while(count==items.length)// 判断计数器是否已到数组长度(是否已满)
notFull.await(); //满了则生产者等待
items[putptr] = x; //按照角标将商品存储在数组中
if(++putptr == items.length)//如果存储的角标到了数组的长度,就将角标归零
putptr = 0;
++count; //计数器自增
notEmpty.signal();//唤醒一个消费者
}
final
{
lock.unlock();
}
}
public Object take() throws InterruptedException
{
lock.lock();
try
{
while(count==0)//如果计数器为0,说明没有商品,消费者等待
notEmpty.await();
Object x = items[takeptr];//从数组中通过消费者角标获取商品
if(++takeptr == items.length) //如果消费的角标等于了数组的长度,将角标归零
takeptr = 0;
--count; //计数器自减
notFull.signal(); //唤醒生产者
return x;
}
final
{
lock.unlock();
}
}
}
- String类
- 字符串都是对象
- 一旦初始化就不可以被更改, 因为是常量
- 通过String类的构造函数可知,将字节数组或者字符转成字符串
public class StringDemo {
public static void main(String[] args) {
// 定义一个字符串
String str = "abc";
System.out.println("str=" + str);
// ----------------
System.out.println("--------多个引用指向同一字符串--------");
String s1 = "itcast";
String s2 = "itcast";
System.out.println(s1 == s2);
System.out.println("--------两个内容相同,创建方式不同的字符串----");
String s3 = "abc";
String s4 = new String("abc");
/*
* s3 s4不同:
* s3创建,在内存中只有一个对象
* s4创建,在内存中有两个对象
* */
System.out.println(s3 == s4); //false
//因为String复写了equals方法,建立字符串自己的判断相同的依据:通过字符串对象中的内容判断
System.out.println(s3.equals(s4)); //true
System.out.println("------字符串长度-------");
String s5 = "abcdeacdb";
System.out.println("len=" + s5.length());
System.out.println("------字符串中某字符的位置------");
int index = s5.indexOf('a'); // a第一次出现的位置
System.out.println("'a'第一次出现的位置:" + index); //索引从0开始
int index1 = s5.indexOf('a', index + 1); // a第二次出现的位置
System.out.println("'a'第二次出现的位置:" + index1); //如果要找到字符不存在,则返回-1
System.out.println("------获取指定位置上字符------");
String s6 = "itcast";
char ch = s6.charAt(3); // 不存在角标则返回StringIndexOutOfBoundsException
System.out.println("ch=" + ch);
System.out.println("------获取部分字符串------");
String s7 = "welcometojavaworld";
String s8 = s7.substring(2, 4); //包含头不含尾
System.out.println("截取后为:" + s8);
}
}
运行结果
- 对象比较用compareTo方法
- StringBuffer
- 是一个字符串缓冲区(容器)
- 长度可变,可任意类型(是将任意数据都转成字符串进行存储)
- 容器对象提供很多对容器中数据的操作功能,如增删查改
- 必须所有数据最终转换为一个字符串
- 和字数组最大不同是:数组存储完可以单独操作每一个元素,每一个元素都是独立的;字符串缓冲区,所有存储的元素都被转为字符串,最后拼成一个大的字符串
public class StringBufferDemo01 {
public static void main(String[] args) {
// 创建一个字符缓冲区对象,用于存储数据
StringBuffer sb = new StringBuffer();
// 添加数据。不断添加数据后,要对缓冲区的最后的数据进行操作,必须转为字符串
String str = sb.append(true).append("hello").toString();
System.out.println("数据:" + str);
System.out.println("---插入数据---");
StringBuffer sb1 = sb.insert(2, "it");
System.out.println("str: " + str);
System.out.println("插入后:" + sb1);
System.out.println("---删除数据---");
System.out.println("str: " + str);
StringBuffer sb2 = sb.delete(1, 4);
System.out.println("删除后: " + sb2);
System.out.println("---替代数据---");
System.out.println("str:" + str);
StringBuffer sb3 = sb.replace(1, 4, "cast");
System.out.println("替代后: " + sb3);
System.out.println("---设置长度(多余部分删除)---");
System.out.println("str: " + str);
sb.setLength(2);
System.out.println();
}
}
运行结果
- StringBuffer小练习
public class StringBufferDemo02 {
public static void main(String[] args) {
/*
* int[] arr = [34, 12, 89, 68]
* 将一个int[]中元素转为字符串,格式为 [34, 12, 89, 68]
*
* */
// 缓冲区的应用:无论多少数据,什么类型,只要最终变成自字符串就可以使用StringBuffer这个容器
int[] arr = {34, 12, 89, 68};
String str = toString(arr);
System.out.println(str);
}
public static String toString(int[] arr) {
// 创建缓冲区
StringBuffer sb = new StringBuffer();
sb.append("[");
for (int i = 0; i < arr.length; i++) {
if (i != arr.length - 1) {
sb.append(arr[i] + ",");
} else {
sb.append(arr[i] + "]");
}
}
return sb.toString();
}
}
运行结果
- StringBuffer和StringBuilder异同
- 功能完全相同
- 区别:
- StringBuilder是非同步的,单线程访问效率高
- StringBuffer是同步的,多线程访问安全
- 基本类型包装类
- 将基本数据类型值封装成对象,可以提供更多的基本操作数值的功能
byte | Byte |
---|---|
short | Short |
int | Integer |
long | Long |
float | Float |
double | Double |
boolean | Boolean |
char | Character |
-
基本数据类型包装类特点
- 用于在基本数据和字符串之间进行转换!
解析方法: int parseInt(string); byte parseByte(string); boolean parseBoolean(string); xxx parseXxx(string); 注:只有Character没有解析方法 ----------------------- 字符串--->基本数值 System.out.println(Integer.parseInt("123")+2); // 125 System.out.println(Integer.parseInt("a1", 16); // 将十六进制“a1”转为十进制 ----------------------- 基本数值--->字符串 String.valueOf(35); Integer.toString(int); ----------------------- 基本数值--->包装对象 Integer i = new Integer(4); Integer ii = new Integer("4"); Integer iii = Integer.valueOf(4); ----------------------- 包装对象--->基本数值 int num = i.intValue();
System.out.println(Integer.MAX_VALUE); System.out.println(Integer.toBinaryString(-6)); // 十进制转二进制 System.out.println(Integer.toHexString(-6)); // 十进制转为十六进制 System.out.println(Integer.toOctalString(-6)); // 十进制转八进制
-
JDK1.5之后为简化书写引入包的新特性,自动装箱拆箱
Integer i = 4; // 自动装箱。原理:Integer i = Integer i = Integer.valueOf(4);
i = i + 5; // 原理: 等号右边:将i对象转为基本数值 i.intValue()+5;//自动拆箱。加法运算后,再次拆箱
- 细节
Integer x = 100;
Integer y = 100;
System.out.println(x == y); // true
System.out.println(x.equals(y)); // true
----------------------------------------------------
Integer x = 128;
Integer y = 128;
System.out.println(x == y); // false
System.out.println(x.equals(y)); // true
----------------------------------------------------
原因: 在JDK1.5中,如果数值在byte(-128~127)之内,不会新创建对象空间而是使用原来已有的空间
- 基本类型包装类练习
import java.util.Arrays;
public class WrapperTest {
/*
* 将“23 9 -4 18 100 7”按照从小到大排序,生成一个数值有序的字符串
*/
private static final String SPACE = " ";
public static void main(String[] args) {
String numsString = "23 9 -4 18 100 7";
numsString = sortNumberString(numsString);
System.out.println("nums = " + numsString);
}
private static String sortNumberString(String numsString) {
// 获取字符串中数字
String[] strs = numsString.split(SPACE);
// 将字符串转为int数组
int[] nums = parseIntArray(strs);
// 对数组进行排序
Arrays.sort(nums);
// 将数组转为字符串
return toString(nums);
}
private static String toString(int[] nums) {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < nums.length; i++) {
if (i != nums.length - 1) {
sb.append(nums[i] + " ");
} else {
sb.append(nums[i]);
}
}
return sb.toString();
}
private static int[] parseIntArray(String[] strs) {
// 定义一个int数组
int[] arr = new int[strs.length];
// 遍历字符串数组,将元素转成int存储到arr中
for (int i = 0; i < strs.length; i++) {
arr[i] = Integer.parseInt(strs[i]);
}
return arr;
}
}
运行结果
- 集合
-
当有多个数据且数据个数不确定,可以使用集合,集合长度可变,集合里面存储的都是对象,并且对象类型可以不一致,可以有重复元素
-
集合中存储到其实是对象的地址
-
集合不可以存储基本数值,但JDK1.5后可以写,但实际存储的还是对象(基本数据类型包装类的对象),如coll.add(3); //coll.add(Integer.valueOf(3)); // 自动装箱
-
存储时存储的对象提升为Object,取出时要使用元素的特有内容,必须向下转型
-
元素取出方式
-
Collection:
- List: 有序的,带索引的,通过索引就可以精确的操作集合中的元素,元素可以重复。List获取元素的方法有两种,一种是迭代,一种是遍历+get。List接口支持对元素进行增删改查
增加 add(element) add(index, element)
删除 remove(element) remove(index)
修改 set(index, element)
查询 get(index)- Vector: 可以增长的数组结构,同步的
- ArrayList: 数组结构,长度可变(原理为创建新数组+复制数组),查询很快,增删较慢,不同步的
- LinkedList: 链表结构,增删速度很快。可用于实现堆栈、队列
堆栈:先进后出,First in Last Out(FILO)
队列:先进先出,First in First Out ( FIFO)
- Set : 不包含重复元素,不保证顺序。方法和Collection一致。Set集合只能通过迭代器取出元素
-
HashSet: 哈希表结构,不同步,保证元素唯一性的方式依赖于:hashCode()、equals()方法。查询速度快
提高唯一性元素的查询效率,还想有序,可使用HashSet的子类LinkedHashSet -
TreeSet: 可以对Set集合中的元素进行排序。使用的是二叉树结构,保证元素唯一性:使用的对象比较方法的结果是否为0,若是0则视为相同元素不存储
元素的排序比较有两种方式:- 元素自身具备自然排序,其实是实现了Comparable接口重写了compareTo方法。如果元素自身不具备自然排序,或者具备的自然排序不是所需要的,这时只能用第二种方式
- 比较器排序,其实是在创建TreeSet集合时,在构造函数中制定具体的比较方式。需要定义一个类实现Comparator接口,重写compare方法
至此,在往集合中存储对象时,通常该对象都需要覆盖hashCode、equals,同时实现Comparable接口,建立对象的自然排序。通常还有一个方法也会复写:toString()
-
- List: 有序的,带索引的,通过索引就可以精确的操作集合中的元素,元素可以重复。List获取元素的方法有两种,一种是迭代,一种是遍历+get。List接口支持对元素进行增删改查
8. 在迭代过程中,不能使用集合的方法操作元素,可使用迭代器的子接口ListIterator操作
- 通过List接口中的ListIterator()可以获取,该列表迭代器只有List接口有,且此迭代器可以完成在迭代过程中的增删改查动作
比较器小练习
// ComparatorByLength.java
package CompareDemo;
// 导入接口
import java.util.Comparator;
public class ComparatorByLength implements Comparator {
@Override
public int compare(Object o1, Object o2) {
String s1 = (String) o1;
String s2 = (String) o2;
int temp = s1.length() - s2.length();
return temp == 0 ? s1.compareTo(s2) : temp;
}
}
// TreeSetTest.java
import CompareDemo.ComparatorByLength;
import java.util.Iterator;
import java.util.Set;
import java.util.TreeSet;
public class TreeSetTest {
/*
* 要求:对字符串进行长度排序(由短到长)
* 思路分析:字符串之所以可以排序,因为是已经实现Comparable接口重写compareTo方法,建立了字符串的自然排序
* 但是自然排序不是需求中所需要的,需要自定义一个比较器
* */
public static void main(String[] args) {
Set set = new TreeSet(new ComparatorByLength());
set.add("abc");
set.add("haha");
set.add("xixi");
set.add("hiahia");
set.add("z");
for (Iterator it = set.iterator(); it.hasNext(); ) {
System.out.println(it.next());
}
}
}
LinkedHashSetDemo
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.Set;
public class LinkedHashSetDemo {
public static void main(String[] args) {
/*
* 提高唯一性元素的查询效率,还想有序,可使用HashSet的子类LinkedHashSet
*/
Set set = new LinkedHashSet();
set.add("abcd");
set.add("hahahah");
set.add("java");
set.add("itcast");
for (Iterator it = set.iterator(); it.hasNext(); ) {
System.out.println(it.next());
}
}
}
- foreach循环 : 其实是增强for循环
- 格式: for(元素的数据类型 变量:Collection集合or数组){ }
- 用于遍历Collection和数组,通常只能遍历元素,不要在遍历的过程中做对集合元素的操作
- 和普通for循环区别:新for循环必须有被遍历的目标,目标只能是Collecion或者是数组。
- 遍历数组时,如果仅为遍历,可以使用增强for循环;如果要对数组的元素进行操作,使用普通for循环,因为可以通过索引操作
List list = new ArrayList();
list.add("a1");
list.add("a2");
list.add("a3");
// 普通
for(Iterator it = list.iterator();it.hasNext();){
Object obj = it.next();
System.out.println(obj);
}
// 新式
for(Object obj: list){
System.out.println(obj);
}
上一篇:Java基础记录(四)
下一篇:Java基础记录(六)